Ready for some heresy? Let’s validate a Backbone.js model using the jQuery Validation plugin and a View.

If you are interested in the heresy, skip to the bottom.

Here is a simple sign up form for a Rails app. We want to validate that the user has entered an email address and filled in the password. Additionally, we would like to know if the email entered is available, so we will use AJAX to call down to the server to check availability dynamically.

 1<form accept-charset="UTF-8" action="#" class="validate" id="new_user" method="post">
 2  <div style="margin:0;padding:0;display:inline">
 3    <input name="utf8" type="hidden" value="">
 4    <input name="authenticity_token" type="hidden" value="dkjfhioeutgdkjdkfj">
 5  </div>
 6  <p>
 7    <label for="user_email">Your email</label>
 8    <input class="text required email" id="user_email" name="user[email]" size="30" type="email" value="">
 9  </p>
10  <p>
11    <label for="user_password">Choose a password</label>
12    <input autocomplete="off" class="text required" id="user_password" name="user[password]" size="30" type="text">
13  </p>
14  <p class="buttons">
15    <input name="commit" type="submit" value="Get started">
16  </p>
17</form>

As a starting point, this is how we might use Validation with jQuery to validate our form.

 1$('#new_user').validate({
 2  rules: {
 3    'user[email]': {
 4      required: true,
 5      email: true,
 6      remote: '/users/email_available'
 7    },
 8    'user[password]': {
 9      required: true,
10      minlength: 6
11    }
12  },
13  messages: {
14    'user[email]': {
15      remote: $.format('{0} has already been taken')
16    }
17  },
18  submitHandler: function(form) {
19    var form = $(form);
20    var form_data = form.formParams()
21    $.ajax({
22      url: form.attr('action'),
23      dataType: 'json',
24      type: "POST",
25      data: form_data,
26      success: function (data, textStatus, xhr) {
27        // code for cleaning up form and showing response here...
28      },
29      error: function (xhr, textStatus, errorThrown) {
30        // code for showing errors here...
31      }
32    });
33  }
34});

If you have not used the Validation plugin before, it is a simple declarative way to validate forms. You specify the rules which apply to your form’s input elements and they are checked as the user enters data and on submit. All the usual validations are provided by the plugin, including remote which will make an Ajax call to your server to check the input. Messages for failed validations can be customized. And there are quite a few options available to change the behavior of the plugin.

Now lets do this with a Backbone.js View.

We will create a basic FormView class to handle both create and edit, using a templated form. That is the extent of our brief for this blog post, a much more complete class is left as an exercise for the reader.

Oh and we are using CoffeeScript.

 1class FormView extends Backbone.View
 2  initialize: ->
 3    @template = _.template($(@templateSelector).html())
 4    @validationOptions = @options.validationOptions
 5
 6  fetchParams: (form) ->
 7    rawdata = $(form).serialize()
 8    data = $.deparam(rawdata)
 9    data['user']
10
11  render: ->
12    template_values = if @model then @model.toJSON() else {}
13    $(@el).html(@template(template_values))
14    opts = { submitHandler: @submit }
15    opts = _.extend(opts, @validationOptions)
16    $(@el).find('form').validate(opts)
17    this
18
19  submit: (form) =>
20    if @model
21      @model.save(fetchParams(form))
22    else
23      @collection.create(fetchParams(form))
24
25class User extends Backbone.Model
26
27users = new Collection
28  model: User
29  url: '/users'
30
31view = new FormView
32  collection: users
33  el: $('#signup_form')
34  templateSelector: '#new_user_form'
35  validationOptions:
36    rules:
37      'user[email]':
38        remote: '/users/email_available'
39      'user[password]':
40        minlength: 6
41    messages:
42      'user[email]':
43        remote: $.format('{0} has already been taken')
44
45view.render()

The FormView class can handle either creating a new model or editing an existing model. When creating a new object pass in the collection as an option, and when editing an existing model pass in the model as an option. You need to specify the el where the FormView will render the form, and the templateSelector that will identify the Underscore.js template. And of course you need to specify the validation rules, messages, and options for the Validation plugin, but not a submitHandler.

Fire things up by calling render, and our form will now validate and if successfully validated, submit. Done!

Here are some things to note:

We are using classes on the inputs to indicate the basic Validation rules to apply.

The validationOptions do not include the submitHandler, which is added by the FormView.

We are also using the jquery.ba-bbq plugin for the deparam method it adds to jQuery1.

Heresy!

On to the heresy. MVC dogma would say that the model should be responsible for its validation, and here the view is doing the validation — the Validation plugin is part of the FormView view internals.

Pragmatism

The existing model validation scheme for Backbone.js is weak. There is no easy declarative way to validate models at the moment. I am hopeful that Derrick Bailey’s Backbone.ModelBinding will eventually lead to an answer. On the other hand, the jQuery Validation plugin already works and works well with very little coding required.

Does ‘dogma’ even apply?

The remote email validation does not fit neatly into the usual MVC validation scheme. If you have done this in Rails, you no doubt have written code that is not part of the normal validation process to achieve this. What is being validated is a single data field from a model independent of the model itself — and Backbone.js is no different: validate operates on a model instance.

Another way to think of this is that the view is gathering and validating input before passing it onto the model. In this case, the validation is part of the behavior of the form and the individual inputs, with the view doing the availabilty check as soon as there is an email address entered by the user. Once the view is valid, the submit passes the data onto the model. And of course, the model on the server is doing its own validations. And the model on the client? It is a one-liner, just a data container for passing data cleanly along from the view (which is doing the interesting work) to the model on the server. The crux is that the input “validation” is substantially more than just an “email must be unique” validation on the model, it is the user experience around entering potentially incorrect data.

I am not convinced that MVC is misplaced here. Given a different framework, the model’s validations could be flexible enough to really accomodate this scenario. Another possiblity is that this MVC is widget-y and hidden within my Backbone.js view. But for the time being, I am happy to keep shipping and live with this heresy.

1 Why is this not in jQuery? Why is something like this not a standalone plugin?

blog comments powered by Disqus