Baby Steps to Backbone.js: Model Validation
Let's go back for a moment for previous
post where we started to bootstrap some basic Backbone.js applications.
It's very simple now, just gathering all the data and posting the data to
the server.
Any reliable system is almost impossible without
validation. If some field is required or must conform to some particular
rule, it should be validated as soon as possible and validation
information should be displayed to user. The user then applies corrections
and re-submit data.
In case of a `Feedback` model, we are interested
that user always inputs her email and feedback message. Backbone.js
provides very straight forward for models validation. If model requires
validation, it should implement validate method.
So, let's extend our model with validate method.
var Feedback = Backbone.Model.extend({
url: '/feedback',
defaults: {
'email': '',
'website': '',
'feedback': ''
},
validate: function (attrs) {
if (!attrs.email) {
return 'Please fill email field.';
}
if (!attrs.feedback) {
return 'Please fill feedback field.';
}
}
});As you can see, in case of email or feedback is missing, we just simply return string with error message.
To better understand what's going on, let's look on some piece of the code from Backbone.js framework. Namely, to `_validate` method of `Backbone.Model`, which is actually responsible for validation.
_validate: function(attrs, options) {
if (options.silent || !this.validate) return true;
attrs = _.extend({}, this.attributes, attrs);
var error = this.validate(attrs, options);
if (!error) return true;
if (options && options.error) {
options.error(this, error, options);
} else {
this.trigger('error', this, error, options);
}
return false;
}
You can see, if `validate` returns either undefined or null or
false, `_validate` just returns true - meaning the model is valid.
Otherwise, it would check if `options.error` function initialized and
call it, if not model event `error` is triggered.
During the model saving, we typically provide both success and error callbacks. It means, that error callback will be called, if model does not pass validation requirements. Inside the callback, we might decided what to do with errors. Right now, let's just do alert.
var options = {
success: function () {
alert('Thanks for the feedback!');
},
error: function (model, error) {
alert(error);
}
};
var feedback = {
email: this.$('#email').val(),
website: this.$('#website').val(),
feedback: this.$('#feedback').val()
};
this.model.save(feedback, options);Notice that `error` callback receiving model itself as first
argument and error object (one returned from `validate` method) as
second argument. Let's try this code: leave email and feedback fields
empty and press submit button.

There are several drawback of such implementation,
though. First of all, `alert` windows are awful, second if user corrects
email, next time she presses the submit button next alert with another
message appears. This is terrible UX, so let's fix it.
So, we should basically do 2 things: aggregate all errors during validation and apply some nice styles to errors.
Instead of returning simple strings, we'll return an array of objects, containing name of failed and field and message.
validate: function (attrs) {
var errors = [];
if (!attrs.email) {
errors.push({name: 'email', message: 'Please fill email field.'});
}
if (!attrs.feedback) {
errors.push({name: 'feedback', message: 'Please fill feedback field.'});
}
return errors.length > 0 ? errors : false;
}Change the `save` method options, to show errors if any error appeared and hide errors if save was successful.
var me = this;
var options = {
success: function () {
me.hideErrors();
},
error: function (model, errors) {
me.showErrors(errors);
}
};And implement 2 simple methods:
showErrors: function(errors) {
_.each(errors, function (error) {
var controlGroup = this.$('.' + error.name);
controlGroup.addClass('error');
controlGroup.find('.help-inline').text(error.message);
}, this);
},
hideErrors: function () {
this.$('.control-group').removeClass('error');
this.$('.help-inline').text('');
}Let's test the code. As all fields are left empty, it will look like,

As fields are filled and form submitted, all errors are cleared from form.
Conclusions
That was very simple
"baby-step" style of approaching model validation. I would could it,
validation "from the box". Even if it's very useful there are a lot of
different approaches of making even things better. The source code is
availble on github.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





