Skip to content
This repository has been archived by the owner on Jul 12, 2020. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
For #342 - trying to make sense of the validation section in common p…
…roblems
  • Loading branch information
addyosmani committed Mar 31, 2013
1 parent 9fc1642 commit f7cc62b
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 56 deletions.
Binary file modified backbone-fundamentals.epub
Binary file not shown.
40 changes: 23 additions & 17 deletions backbone-fundamentals.md
Expand Up @@ -5592,14 +5592,6 @@ This means that it can be a challenge to determine which specific fields are bei

**Solution**

The most optimal solution to this problem probably isn't to stick validation in your model attributes. Instead, have a function specifically designed for validating that particular form. There are many good JavaScript form validation libraries out there. If you want to stick it on your model, just make it a class function:

```javascript
User.validate = function(formElement) {
//...
};
```

To illustrate this problem better, let us look at a typical registration form use case that:

* Validates form fields using the blur event
Expand Down Expand Up @@ -5639,7 +5631,7 @@ HTML:
</html>
```

Some simple validation that could be written using the current Backbone `validate` method to work with this form could be implemented using something like:
Basic validation that could be written using the current Backbone `validate` method to work with this form could be implemented using something like:


```javascript
Expand All @@ -5652,7 +5644,7 @@ validate: function(attrs) {
}
```

Unfortunately, this method would trigger a first name error each time any of the fields were blurred and only an error message next to the first name field would be presented.
Unfortunately, this method would trigger a `firstname` error each time any of the fields were blurred and only an error message next to the first name field would be presented.

One potential solution to the problem is to validate all fields and return all of the errors:

Expand All @@ -5668,7 +5660,7 @@ validate: function(attrs) {
}
```

This can be adapted into a complete solution that defines a Field model for each input in our form and works within the parameters of our use case as follows:
This can be adapted into a solution that defines a Field model for each input in our form and works within the parameters of our use case as follows:

```javascript

Expand Down Expand Up @@ -5708,11 +5700,12 @@ $(function($) {

```


This works great as the solution checks the validation for each attribute individually and sets the message for the correct blurred field. A [demo](http://jsbin.com/afetez/2/edit) of the above by [@braddunbar](http://github.com/braddunbar) is also available.
This works fine as the solution checks the validation for each attribute individually and sets the message for the correct blurred field. A [demo](http://jsbin.com/afetez/2/edit) of the above by [@braddunbar](http://github.com/braddunbar) is also available.

Unfortunately, this solution does perform validation on all fields every time, even though we are only displaying errors for the field that has changed. If we have multiple client-side validation methods, we may not want to have to call each validation method on every attribute every time, so this solution might not be ideal for everyone.

##### Backbone.validateAll

A potentially better alternative to the above is to use [@gfranko](http://github.com/@franko)'s [Backbone.validateAll](https://github.com/gfranko/Backbone.validateAll) plugin, specifically created to validate specific Model properties (or form fields) without worrying about the validation of any other Model properties (or form fields).

Here is how we would setup a partial User Model and validate method using this plugin for our use case:
Expand Down Expand Up @@ -5804,16 +5797,14 @@ user.set({ 'firstname': 'Greg' }, {validate: true, validateAll: false});

```

That's it!

The Backbone.validateAll logic doesn't override the default Backbone logic by default and so it's perfectly capable of being used for scenarios where you might care more about field-validation [performance](http://jsperf.com/backbone-validateall) as well as those where you don't. Both solutions presented in this section should work fine however.
That's it. The Backbone.validateAll logic doesn't override the default Backbone logic by default and so it's perfectly capable of being used for scenarios where you might care more about field-validation [performance](http://jsperf.com/backbone-validateall) as well as those where you don't. Both solutions presented in this section should work fine however.


##### Backbone.Validation

As we've seen, the `validate` method Backbone offers is `undefined` by default and you need to override it with your own custom validation logic to get model validation in place. Often developers run into the issue of implementing this validation as nested ifs and elses, which can become unmaintainable when things get complicated.

A helpful plugin for Backbone called [Backbone.Validation](https://github.com/thedersen/backbone.validation) attempts to solve this problem by offering an extensible way to declare validation rules on the model and override the `validate` method behind the scenes.
Another helpful plugin for Backbone called [Backbone.Validation](https://github.com/thedersen/backbone.validation) attempts to solve this problem by offering an extensible way to declare validation rules on the model and override the `validate` method behind the scenes.

One of the useful methods this plugin includes is (pseudo) live validation via a `preValidate` method. This can be used to check on key-press if the input for a model is valid without changing the model itself. You can run any validators for a model attribute by calling the `preValidate` method, passing it the name of the attribute as well as the value you would like validated.

Expand All @@ -5824,6 +5815,21 @@ One of the useful methods this plugin includes is (pseudo) live validation via a
var errorMsg = myModel.preValidate('attribute', 'value');
```

##### Form-specific validation classes

That said, the most optimal solution to this problem may not be to stick validation in your model attributes. Instead, you could have a function specifically designed for validating a specific form and there are many good JavaScript form validation libraries out there that can help with this.

If you want to stick it on your model, you can also make it a class function:

```javascript
User.validate = function(formElement) {
//...
};
```

For more information on validation plugins available for Backbone, see the [Backbone wiki](https://github.com/documentcloud/backbone/wiki/Extensions%2C-Plugins%2C-Resources#model).


#### Avoiding Conflicts With Multiple Backbone Versions

**Problem**
Expand Down
28 changes: 17 additions & 11 deletions backbone-fundamentals.rtf
Expand Up @@ -4346,10 +4346,6 @@ documentation
{\pard \ql \f0 \sa180 \li0 \fi0 By default, when we define a custom {\f1 validate} method, Backbone passes all of a model\u8217's attributes through this validation each time, regardless of which model attributes are being set.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This means that it can be a challenge to determine which specific fields are being set or validated without being concerned about the others that aren\u8217't being set at the same time.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Solution}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 The most optimal solution to this problem probably isn\u8217't to stick validation in your model attributes. Instead, have a function specifically designed for validating that particular form. There are many good JavaScript form validation libraries out there. If you want to stick it on your model, just make it a class function:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 User.validate = function(formElement) \{\line
//...\line
\};\par}
{\pard \ql \f0 \sa180 \li0 \fi0 To illustrate this problem better, let us look at a typical registration form use case that:\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Validates form fields using the blur event\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Validates each field regardless of whether other model attributes (i.e., other form data) are valid or not.\sa180\par}
Expand Down Expand Up @@ -4381,15 +4377,15 @@ documentation
</form>\line
</body>\line
</html>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Some simple validation that could be written using the current Backbone {\f1 validate} method to work with this form could be implemented using something like:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Basic validation that could be written using the current Backbone {\f1 validate} method to work with this form could be implemented using something like:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 validate: function(attrs) \{\line
\line
if(!attrs.firstname) return 'first name is empty';\line
if(!attrs.lastname) return 'last name is empty';\line
if(!attrs.email) return 'email is empty';\line
\line
\}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Unfortunately, this method would trigger a first name error each time any of the fields were blurred and only an error message next to the first name field would be presented.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Unfortunately, this method would trigger a {\f1 firstname} error each time any of the fields were blurred and only an error message next to the first name field would be presented.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 One potential solution to the problem is to validate all fields and return all of the errors:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 validate: function(attrs) \{\line
var errors = \{\};\line
Expand All @@ -4400,7 +4396,7 @@ documentation
\line
if (!_.isEmpty(errors)) return errors;\line
\}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This can be adapted into a complete solution that defines a Field model for each input in our form and works within the parameters of our use case as follows:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This can be adapted into a solution that defines a Field model for each input in our form and works within the parameters of our use case as follows:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 \line
$(function($) \{\line
\line
Expand Down Expand Up @@ -4435,14 +4431,15 @@ $(function($) \{\line
\});\line
\line
\});\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This works great as the solution checks the validation for each attribute individually and sets the message for the correct blurred field. A {\field{\*\fldinst{HYPERLINK "http://jsbin.com/afetez/2/edit"}}{\fldrslt{\ul
{\pard \ql \f0 \sa180 \li0 \fi0 This works fine as the solution checks the validation for each attribute individually and sets the message for the correct blurred field. A {\field{\*\fldinst{HYPERLINK "http://jsbin.com/afetez/2/edit"}}{\fldrslt{\ul
demo
}}}
of the above by {\field{\*\fldinst{HYPERLINK "http://github.com/braddunbar"}}{\fldrslt{\ul
@braddunbar
}}}
is also available.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Unfortunately, this solution does perform validation on all fields every time, even though we are only displaying errors for the field that has changed. If we have multiple client-side validation methods, we may not want to have to call each validation method on every attribute every time, so this solution might not be ideal for everyone.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs20 Backbone.validateAll\par}
{\pard \ql \f0 \sa180 \li0 \fi0 A potentially better alternative to the above is to use {\field{\*\fldinst{HYPERLINK "http://github.com/@franko"}}{\fldrslt{\ul
@gfranko
}}}
Expand Down Expand Up @@ -4527,14 +4524,13 @@ var User = Backbone.Model.extend(\{\line
{\pard \ql \f0 \sa180 \li0 \fi0 It\u8217's fairly straight-forward to use as well. We can simply define a new Model instance and then set the data on our model using the {\f1 validateAll} option to use the behavior defined by the plugin:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 var user = new User();\line
user.set(\{ 'firstname': 'Greg' \}, \{validate: true, validateAll: false\});\par}
{\pard \ql \f0 \sa180 \li0 \fi0 That\u8217's it!\par}
{\pard \ql \f0 \sa180 \li0 \fi0 The Backbone.validateAll logic doesn\u8217't override the default Backbone logic by default and so it\u8217's perfectly capable of being used for scenarios where you might care more about field-validation {\field{\*\fldinst{HYPERLINK "http://jsperf.com/backbone-validateall"}}{\fldrslt{\ul
{\pard \ql \f0 \sa180 \li0 \fi0 That\u8217's it. The Backbone.validateAll logic doesn\u8217't override the default Backbone logic by default and so it\u8217's perfectly capable of being used for scenarios where you might care more about field-validation {\field{\*\fldinst{HYPERLINK "http://jsperf.com/backbone-validateall"}}{\fldrslt{\ul
performance
}}}
as well as those where you don\u8217't. Both solutions presented in this section should work fine however.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs20 Backbone.Validation\par}
{\pard \ql \f0 \sa180 \li0 \fi0 As we\u8217've seen, the {\f1 validate} method Backbone offers is {\f1 undefined} by default and you need to override it with your own custom validation logic to get model validation in place. Often developers run into the issue of implementing this validation as nested ifs and elses, which can become unmaintainable when things get complicated.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 A helpful plugin for Backbone called {\field{\*\fldinst{HYPERLINK "https://github.com/thedersen/backbone.validation"}}{\fldrslt{\ul
{\pard \ql \f0 \sa180 \li0 \fi0 Another helpful plugin for Backbone called {\field{\*\fldinst{HYPERLINK "https://github.com/thedersen/backbone.validation"}}{\fldrslt{\ul
Backbone.Validation
}}}
attempts to solve this problem by offering an extensible way to declare validation rules on the model and override the {\f1 validate} method behind the scenes.\par}
Expand All @@ -4543,6 +4539,16 @@ Backbone.Validation
// if not, it returns a falsy value\line
\line
var errorMsg = myModel.preValidate('attribute', 'value');\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs20 Form-specific validation classes\par}
{\pard \ql \f0 \sa180 \li0 \fi0 That said, the most optimal solution to this problem may not be to stick validation in your model attributes. Instead, you could have a function specifically designed for validating a specific form and there are many good JavaScript form validation libraries out there that can help with this.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 If you want to stick it on your model, you can also make it a class function:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 User.validate = function(formElement) \{\line
//...\line
\};\par}
{\pard \ql \f0 \sa180 \li0 \fi0 For more information on validation plugins available for Backbone, see the {\field{\*\fldinst{HYPERLINK "https://github.com/documentcloud/backbone/wiki/Extensions%2C-Plugins%2C-Resources#model"}}{\fldrslt{\ul
Backbone wiki
}}}
.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Avoiding Conflicts With Multiple Backbone Versions\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Problem}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 In instances out of your control, you may have to work around having more than one version of Backbone in the same page. How do you work around this without causing conflicts?\par}
Expand Down

0 comments on commit f7cc62b

Please sign in to comment.