Form Validation with Breeze and Aurelia

One of the great things about Breeze is it's built-in validation engine.

Support for validation is built into Breeze entities and the Breeze processing pipelines. Breeze maintains lists of validation rules and applies them automatically at specific moments in the entity life-cycle. But you remain always in control. Breeze discovers some validation rules and infers others; but you can add your own rules and remove any rule that you don't like. You choose when Breeze validates. You can validate any set of entities or entity properties at any time. You decide what the error messages say and how they're presented.

--breeze docs

Developers using Breeze often need to integrate the Breeze validation errors with input elements on an HTML form. This involves subscribing to the breeze Entity's (or EntityManager's) validationErrorsChanged event and displaying the invalid state on the appropriate form input for property-level errors or in a "validation summary" for entity-level validation errors.

Developers using ng1 could automate part of this task by using the zValidate directive from breeze labs. By adding the data-z-validate to each form input the property-level validation errors would be displayed automatically.

aurelia-breeze

The aurelia-breeze plugin now ships with an experimental custom attribute that automates the task of displaying all property-level and entity-level validation errors.

Consider the following bootstrap-style form bound to a Breeze "Person" entity:

<form submit.delegate="submit()">  
  <div class="form-group">
    <label for="first-name">First Name</label>
    <input type="text" value.bind="entity.firstName"
           class="form-control" id="first-name" placeholder="First Name">
  </div>
  <div class="form-group">
    <label for="last-name">Last Name</label>
    <input type="text" value.bind="entity.lastName"
           class="form-control" id="last-name" placeholder="Last Name">
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>  

We can display validation errors in this form by adding the new breeze-validation custom attribute to the form:

<form submit.delegate="submit()" breeze-validation.bind="entity">  
  ...
</form>  

The rest of the form markup does not need to change!

This is made possible by Aurelia's powerful templating and binding engines. The breeze-validation attribute is able to locate the two-way bindings on the form, determine whether the binding is to a Breeze entity property and discover the name of the property- even in complex binding expressions using value-converters!

The breeze-validation attribute can be bound to a single breeze Entity or to a breeze EntityManager instance for scenarios where your form is bound to multiple entities or validation errors could arise from other entities that aren't used in the form.

Here's a working example:

Do I have to use bootstrap?

No. The logic that displays the validation errors lives in an implementation of the ErrorRenderer interface and is separate from the core logic of the breeze-validation attribute. The aurelia-breeze plugin ships with a BootstrapErrorRenderer which you could use as the starting point for creating your own error rendering logic. For example, if you created a FoundationErrorRenderer that uses the Zurb Foundation css, you could register it in your Aurelia application's main.js by doing following:

import {ErrorRenderer} from 'aurelia-breeze';  
import {FoundationErrorRenderer} from './foundation-error-renderer';

export function configure(aurelia) {  
  ...
  ...
  aurelia.container.registerTransient(ErrorRenderer, FoundationErrorRenderer); 
  ...
  ...
}

Do I need a <form> element?

No. You can put the breeze-validation attribute on any element and it will wire up the validation for any two-way bindings in the descendent elements.

Can I customize the validation messages?

Yes. Breeze offers a number of ways to customize validation messages. Check out this issue for more info.

Experimental

This is an experimental feature and will change based on community feedback. Give it a try and let me know what you think. Issues can be reported here.