Aurelia validation: a step by step setup guide

by Roman Fürst
Tags: JavaScript , Aurelia , Tutorial , Open Source , TypeScript , Validation

Today I'm getting my hands dirty with the recently announced Aurelia 1.0 release and especially its new validation alpha. In this tutorial we are going to create a basic application using the all-new Aurelia-cli and set up form validation with the help of aurelia's validation library.

Attention: aurelia-validation has gone through major changes since releasing this article. See this updated post for a more recent version.

Create a new project

Let's get started by creating a new project. But first we need to install aurelia-cli using npm:

$ sudo npm install -g aurelia-cli

Then simply call au new aurelia-validation-tutorial from your console and follow the instructions. I'm going with a default TypeScript setup here, but you are free to choose whatever fits you best. It doesn't really matter, the concepts are all the same. It's just the syntax that might differ a bit. You will be prompted whether you want to create the project and whether all the project dependencies should be installed. Confirm both questions with Yes. cd into the new created directory and start Aurelia:

$ cd aurelia-validation-tutorial
$ au run --watch

Alright, that's it! You have now a production ready Aurelia skeleton in place. Wasn't that easy? Of course it was, so let's jump right into the next part.

Set up aurelia validation

Installing dependencies

Let's start by installing the required dependencies:

$ npm install aurelia-validation aurelia-validatejs --save

aurelia-validation is basically a generic validation interface whereas aurelia-validatejs is a concrete implementation powered by validate.js. We also append --save to include our new dependencies in the project's package.json.

Next we will have to add these libraries to our project. This step will become obsolete once aurelia-cli supports the installation of 3rd party libraries. Until then we just append a few lines under dependencies of our vendor-bundle in aurelia_project/aurelia.json:

{
    "name": "aurelia-validation",
    "path": "../node_modules/aurelia-validation/dist/amd",
    "main": "aurelia-validation"
},
{
    "name": "aurelia-validatejs",
    "path": "../node_modules/aurelia-validatejs/dist/amd",
    "main": "aurelia-validatejs"
},
{
    "name": "validate.js",
    "path": "../node_modules/validate.js/",
    "main": "validate"
}

TypeScript users will also have to install a d.ts file. Thankfully the guys at Aurelia are a step ahead:

$ typings install github:aurelia/validatejs/lib/validatejs.d.ts --global --save

Finally we register aurelia-validation and aurelia-validatejs as plugins. Open src/main.ts and update Aurelia's configuration:

aurelia.use
  .standardConfiguration()
  .feature("resources")
  .plugin("aurelia-validation")
  .plugin("aurelia-validatejs");

Note: At this point Aurelia will fail to start up. This is because we haven't instanciated a ValidationController yet. We will fix this in the next step.

Creating a simple form

Now that we have our dependencies installed, we are going to create a form that holds our data and two custom elements which collect user input and contain our validation rules. This way we will end up having reusable, loosely coupled custom elements that can be validated out of a parent element, the actual form in our case.

src/resources/elements/person-detail.ts

Let's create our first custom input element:

export class PersonDetail {
  @bindable
  public person;

We use the bind() callback to hook in our validation rules using the fluent API. There are many other validator beside required(). Take a look at the official validate.js docs. You can also pass additional options to a validator. Here we are passing a custom error message that should be rendered in case of a validation error. By the way: Did you notice the ^ character in front of the message? This tells validate.js to not automatically include the property name. Without this in place, the error would be rendered like "last name My custom error message".

  public bind() {
    ValidationRules
      .ensure("firstName").required()
      .ensure("lastName").required({ message: "^My custom error message" })
      .on(this.person);
  }
}

src/resources/elements/person-detail.html

The view is straight-forward. Just remember to include the validate binding behavior. The error div is where we will render error messages later on.

<template>
  <div class="first-name">
    <label for="first">First name:</label>
    <input id="first"  type="text" value.bind="person.firstName & validate"/>
    <span class="error"></span>
  </div>
  <div class="last-name">
    <label for="last">Last name:</label>
    <input id="last" type="text" value.bind="person.lastName & validate"/>
    <span class="error"></span>
  </div>
</template>

src/resources/elements/country-detail.ts

Now the second custom input element. This time we define our validation rules using the decorator API.

export class CountryDetail {
  @bindable
  @required({ message: "^Another custom error message." })
  public name: string;
}

src/resources/elements/country-detail.html

<template>
  <div class="country-name">
    <label for="name">Name:</label>
    <input id="name" type="text" value.bind="name & validate"/>
    <span class="error"></span>
  </div>
</template>

src/app.ts

Finally we make src/app.ts our parent view-model that holds our data. We also inject an instance of ValidationController using the NewInstance resolver. This is telling Aurelia to always inject a new instance of ValidationController instead of sharing it with other components. This also has the effect that whenever we call validate() on this specific ValidationController instance, all children elements (PersonDetail and CountryDetail in our case) will be automagically validated.

@inject(NewInstance.of(ValidationController))
export class App {

  public person = {
    firstName: "",
    lastName: ""
  }

  public country = "";

  constructor(private validationController: ValidationController) {}

  public submit() {
    if (this.validationController.validate().length <= 0) {
      alert("Validation successful!");
    } else {
      alert("Validation failed!");
    }
  }
}

src/app.html

The view is straight-forward again.

<template>
  <require from="resources/elements/person-detail"></require>
  <require from="resources/elements/country-detail"></require>

  <form submit.delegate="submit()">
    <h1>Person</h1>
    <person-detail person.bind="person"></person-detail>
    <h1>Country</h1>
    <country-detail name.bind="country"></country-detail>

    <button type="submit">Submit</button>
  </form>
</template>

The error that prevented Aurelia from starting up should be resolved at this point. We just instanciated our ValidationController. Test the application by running au run in your console. Works? Great!

Writing a custom error message renderer

But wait, where are my error messages? We aren't there yet. To display error messages we need to create a ValidationRenderer. Let's call our custom renderer SimpleValidationRenderer in this tutorial.

src/resources/validation/simple-validation-renderer.ts

First we indicate to Aurelia that this class is a validation renderer using a decorator.

@validationRenderer
export class SimpleValidationRenderer {

Remember the error divs in our two custom input elements? This is exactly where we are going to render error messages. error is, who would have thought it, the actual error whereas target is the DOM element this error belongs to.

  public render(error: ValidationError, target: Element): void {
    target.parentElement.querySelector(".error").textContent = error.message;
  }

Once validation becomes successful, we need to remove all the error messages. We do this by implementing a simple unrender() method.

  public unrender(error: ValidationError, target: Element): void {
    target.parentElement.querySelector(".error").textContent = "";
  }
}

We're almost there. We just need to register our SimpleValidationRenderer with Aurelia and then we need to tell our form which renderer to use. Registering a handler is done by adding the following snippet just before Aurelia starts in src/main.ts:

src/main.ts

aurelia.container.registerHandler("simple-renderer", container => container.get(SimpleValidationRenderer));

And telling our form to use this renderer is as simple as adding an attribute to our <form> HTML element:

src/app.html

<form submit.delegate="submit()" validation-renderer="simple-renderer">

We're done! Now start the application and see Aurelia's form validation in its full glory.

Bonus tip

By default validation is triggered in the DOM blur event. You can change this behavior by simply assigning ValidationController.validateTrigger. For example, if you do not want to trigger validations automatically, set ValidationController.validateTrigger to validateTrigger.manual. There is also an option to validate inputs in the DOM change event.

import {validateTrigger} from 'aurelia-validation';
//...

constructor(private validationController: ValidationController) {
  validationController.validateTrigger = validateTrigger.manual;
}
//...

Source code

You can find the full source code for this tutorial on github. If you have any questions, suggestions or whatever, feel free to write a comment below. I hope you liked it and see you next time.