Monday, October 3, 2011

Backbone.js Validations

‹prev | My Chain | next›

It seems as though I have my entire Backbone.js calendar application working with faye. I can create, modify, delete appointments any number of times and everything just works. In the end switch Backbone persistence layers was pretty easy. My problems were either of the broke-in-regular-transport variety or were related to reading the correct CouchDB meta attributes (e.g. "_id" or "_rev").

Anyhow, with everything working, it is time to switch back to the regular transport and begin exploring other facets of Backbone. Up tonight: validations.

Validations in Backbone are fairly trivial. I simply need a method on my model named validate:
        var Appointment = Backbone.Model.extend({
          // ...
          validate: function(attributes) {
            var errors = [];

            if (!(/\S/.test(attributes.title)))
              errors.push("Title cannot be blank.");

            if (!/\S/.test(attributes.description))
              errors.push("Description cannot be blank.");

            if (errors.length > 0)
              return errors;
          // ...
In here, I use the same regular expression, /\S/ (matches a non-whitespace character) to test both the title and description attributes. If either contains nothing but whitespace (i.e. is blank), then an error string is added to the errors array, which is then returned. If no errors occur, then nothing is returned and validation passes.

Testing this out, I try to create an appointment without a description:
And it fails as expected. Well, it fails as expected, but maybe not quite as desired because nothing happens. There is no visual indication that anything happened at all.

Of course, this is Backbone, so I must need to bind an event handler somewhere to account for this case.

But first, I try updating an existing appointment by blanking out the description. This time, I am greeted with the following:

I added this particular message to the handler bound to the "error" event in my Appointment view:
        var Appointment = Backbone.View.extend({
          // ...
          initialize: function(options) {
            // ...
            options.model.bind('error', this.deleteError, this);
          // ...
          deleteError: function(model, error) {
            if (error.status == 409) {
              alert("This site does not understand CouchDB revisions.");
            else {
              alert("This site was made by an idiot.");
OK so the update error message could be a bit more helpful for the user. And the handler could be named a bit better. But At last I know how to handle that error. But what about create errors?

For that, I define a generic errors handler:
        function handleModelErrors(model, errors) {
And add this to the collection create() call in my appointment-add view:
       var AppointmentAdd = new (Backbone.View.extend({
          // ...
          events: {
            'click .ok':  'create'
          create: function() {
              title: this.el.find('input.title').val(),
              description: this.el.find('input.description').val(),
              startDate: this.el.find('.startDate').html()
            }, {error: handleModelErrors} );
Now, when I try to create an appointment with a blank title and description, I get the alert dialog from my simple callback function:
That will suffice for a stopping point for tonight. Up tomorrow: hopefully a bit more robust a solution than an alert dialog.

Day #152

1 comment:

  1. It is better to have specific handlers for specific cases based on specific events.

    Consider how Rails does model errors: it adds them to an array on the model so a *caller* can access them.

    So, have validate add errors to the model (and clear them out when it begins) and have the view listen to the model "error" event.

    The view's handler function for a model error should be to display the model's errors on the form. Perhaps the model's errors attribute should be an object like:

    {title: 'can not be blank', description: 'can not be blank'}

    Then you can decorate the fields.

    Your generic "handle errors" method should be repurposed into two things:

    1) An error handler in backbone.sync for the couch issues
    2) Methods on Backbone.Model for handling the errors object on a model in nice ways.