Working with Forms
Popular template engines like mustache.js
, Handlebars
or _.template
generate a string. In original Backbone we just replace the bounding element text node with that string. If you have a form in the template its state will be lost with every template update. On the contrary, ngBackbone relies on DOM-based template engine. With every synchronization it doesn't destroy the DOM-subtree, but updates target nodes gracefully. Besides, it takes over the most of routine work we do every time we develop a UI with forms. ngBackbone creates a state model per every input of the specified group and binds them respectively. It subscribes validators for control change/update events and updates the related state models.
Let's see it in action. First we create a simple component that extends FormView
:
Here in the template we have an input control with required
attribute. This attribute makes the component the control to validate against an empty value. Thus until we type anything in the input the underlying element shows up the validation message.
For an end-user it may seem confusing. They haven't typed yet anything, but error message still show up. We can restrict this message to appear only if any of the group controls were touched:
As you can see, during initialization FormView
extracts all the groups marked with data-ng-group
in the template. It extracts all the controls by [name]
for each group and creates state models named as groupName.controlName
for controls and groupName.group
for the group itself.
State model has following properties:
value - control actual value
valid - true if control's value is valid
touched - true if control has been visited
dirty - true if control's value has changed
badInput - a boolean indicating the user has provided input that the browser is unable to convert.
customError - a boolean indicating the element's custom validity message. e.g. custom validator's
stepMismatch - a boolean indicating the value does not fit the rules determined by the step attribute
tooLong- a boolean indicating the value exceeds the specified maxlength
valueMissing - a boolean indicating the element has a required attribute, but no value.
rangeOverflow - a boolean indicating the value is greater than the maximum specified by the max attribute.
rangeUnderflow - a boolean indicating the value is less than the minimum specified by the min attribute.
typeMismatch - a boolean indicating the value is not in the required syntax
patternMismatch - a boolean indicating the value does not match the specified pattern
validationMessage - a string containing validation message
FormView
listens to control input/change events and updates the ControlState models by the actual content of element ValidityState (see HTML5 Form API). So if you have an input <input required>
and until it has a non-empty value the state model property valueMissing
is true
and valid
property is false
, validationMessage
contains localized (by user agent) error message.
Form Validation
HTML provides a number of input types (e.g. email
, tel
, url
, number
) that validate. FormView binds the control ValidityState to the view and therefore we can make advantage of it:
If you need to customize validation message, you can create a container that shows up according the state of account.email.typeMismatch
Moment! But it shows the validation error even before we actually had a chance to provide any input. That can be a bit confusing. Let's improve user experience by showing error message after user started to type in the control (account.email.dirty
is true
)
Custom Validation
HTML5 Form API gives us some options for input validation. But it can be not enough or rather inconvenient to use. With ngBackbone we can pass to the view a map of custom asynchronous validators and refer them though data-ng-validate
attribute:
Every validator must be a callback that returns a Promise. If validation passes the Promise resolves. Otherwise it rejects with error message passed as an argument
We can also target multiple validators per control:
Instead of passing custom validators in object literal we can pass a class extending FormValidators
. That unlocks features available for class members. For example, we can use @Debounce
decorator to debounce validator method call:
In the example above we debounce name validator that requests a remote server. Thus regardless the speed of typing it's called no more than every 350ms - to avoid spawning of slow XHR requests.
Last updated