Menu

A Conventional Blog for Convention Based Coders

Category: Validation

It’s sometimes helpful to know in code if a validation has been performed, or if an individual field has changed its state.

While there are no events currently raised by the ValidationController, we can add our own thanks to some rather snazzy built-in features of Aurelia.

If you’ve read my posts in Gitter, you may have noticed me ranting on about the Event Aggregator, and how frustrated I get at it being misused by people as an event queue, while in reality it’s a very simple pub-sub service. Well in this case the fact that it is simply a pub-sub service is perfect for our needs.

When @jdanyow pointed me to the Event Aggregator code for event handling, my first comment was that the global application doesn’t need to know about every event, only those in the scope of the validation controller, then he pointed out this fascinatingly simple little piece of code:

Now this is awesome. This is a drop-in pub-sub handler re-using the event aggregator, intended for use at a local scope level. At the end of the day the default EventAggregator is exactly the same, implemented as a singleton for our entire application, but that simple instruction lets us inject event handling into any object instance we create.

Almost… unfortunately this is tricky for TypeScript because this code is injecting functions onto an object instance, and people will also argue that this is an anti-pattern, because you’re morphing someone else’s code, but at least it exposes the fact that new instances of Event Aggregators can be used anywhere… so let’s just extend the base EventAggregator class, and use that.

So, let’s apply it to the validation service!

Now where can we go to hear every time validation is performed? A validation Renderer! This is called for every render operation, and is passed in all of the information regarding the result, including the messages, related UI elements, and the reason for the change (this is not yet published, but should be in the next day or so).

So do we want to hack our existing renderer do add this? Well we could, or we could simply add a new instance, as the controller allows us to attach as many renderers as we like.

OK, so let’s create a custom renderer to raise out events:

event-renderer.js

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

exportclassEventRendererextendsEventAggregator{

render(instruction){

this.publish('validate',instruction);

for(let{error,elements}of instruction.unrender){

for(let element of elements){

this.publish('hide',{element,error});

}

}

for(let{error,elements}of instruction.render){

for(let element of elements){

this.publish('show',{element,error});

}

}

}

}

Then we’ll add it to our controller, along with the usual one we want to display our errors, and while we’re at it we’ll add a few subscriptions to monitor various events occurring in the controller:

Remember though that these events can be used to detect changes visually on the screen, but can’t be used to indicate the overall validity of a form, as some errors may be for field with rules which have not yet been validated, as the user has not yet tabbed through or form-validated.

One additional nice feature is that the result above shows 6 ‘render’ errors, but only 5 listed. So where’s the 6th? Well that’s a validation rule against a field that has no UI element, so in my implementation I’m not raising a ‘show’ event. But the error is available through the ‘validate’ event, and if you wanted you could easily generate an additional event for those ‘hidden’ failures:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

for(let{error,elements}of instruction.unrender){

if(!elements.length)

this.publish('hide-non-ui',{null,error});

else

for(let element of elements){

this.publish('hide',{element,error});

}

}

for(let{error,elements}of instruction.render){

if(!elements.length)

this.publish('show-non-ui',{null,error});

else

for(let element of elements){

this.publish('show',{element,error});

}

}

Oh, and don’t forget to dispose of your EventAggregator when you’re finished!

The return value from subscribe is function called dispose() that closes the subscription, and without this call your VM may be left sitting in memory (and may continue to react to published events)

It’s worth noting that function such as required() and maxLength() are simply helpers that point to a predefined satisfies functions, showing that the same features used by rules defined in the library are also available to you in your validation definitions.

So what about when it’s a one-off simple validation rule, not requiring a re-usable generic custom rule? Well there’s even a syntax for that through use of the satisfies() fluent function:

JavaScript

1

2

3

.ensure((m:App)=>m.firstname).displayName("First name")

.required()

.satisfies(v=>v.trim()===v).withMessage('${$displayName} cannot have leading or trailing spaces.')

And to top it all off, both satisfies and custom rules support the return of a Promise for async validation calls, such as making server calls to test username uniqueness, or validating email addresses against existing lists.

Predicates

Sometimes you only want to apply a set of rules if a certain condition has been met in your model. This is referred to as a predicate expression, and one example might be to only validate an email address if the user reports that they want to subscribe to the newsletter:

JavaScript

1

2

3

.ensure((m:App)=>m.email)

.required().when((m:App)=>m.newsletter===true)

.email().when((m:App)=>m.newsletter===true)

This is particularly important when you consider that in this case you may be hiding or excluding the input field if the user is not subscribed to the newsletter, but the validation rule will still be applied because the rule is against the object, not the UI field!
Note that the when condition applies to each rule, and not the overall ‘ensure’ block. This allows finer grain control, albeit it at the expense of slightly more verbose code.

Well the obvious thing is simply style them, but we have some useful additional data available via the controller, such as the control that is linked to the property that was bound to the rule that failed validation…

So we can see from this that the validation controller exposes a Map linking visual elements to validation failures, so we can simply find the first associated element and set focus to that DOM element.
It’s not perfect, as there can be cases where a rule has no visual element, but it will suffice for this demonstration.

So we make sure the template is globally registered:

JavaScript

1

config.globalResources(['./elements/validation-summary.html'])

and we include it at the bottom of our form:

XHTML

1

2

3

4

5

<validation-summary

errors.bind="controller.errors"

controller.bind="controller"

autofocus.bind="true">

</validation-summary>

What do we get? This piece of awesomeness:

You may have noticed this in the middle of the template:<template replaceable part="error">${error.message}</template>

This lets us replace the string with an alternative format in the host view:

Over the last 12 months or so Aurelia has had two different approaches to validation offered as official packages. The first was innovative and reasonably effective, but the code-base was a bit messy, and the original team-member dropped off the map, leaving the team in a tricky situation.

The decision was then made to create a simple interface so that developers could plug in any back-end validation they liked, and an example was created using the Validate.js library. This survived for a few months, but there were many issues related to the underlying engine that made it unsuitable as a solution for the long-term.

Other libraries exist, including Treacherous, which I wrote about here, and this is still available as an option. Now, after many hours of meetings with Jeremy Danyow of the Aurelia core team, we’ve managed to pull in all of the ideas and use-cases learned from these other systems into what we hope is one of the most complete validation solutions out there, let-alone the best for Aurelia.

So let’s take the new version for a spin!

Note that this is example written in Typescript, but if you’re daft enough to sacrifice all that nice type-checking and intellisense and want to use ES6, all you need to do is remove the :type from things (e.g. firstname: string = ''; becomes firstname = '';) and change constructor(private controller: ValidationController) { to say constructor(controller) { this.controller = controller;

The documentation will all be on the Aurelia Hub within the next few hours, but let’s create a really simple example of its use on a form. We’ll start with a simple CLI project, and bodge a simple example into the main form.

First, don’t forget to add aurelia-validation as a plugin in our main.ts, as this is needed even if you’re not using the UI bindings:

JavaScript

1

.plugin('aurelia-validation');

Next, let’s setup the ValidationController and create some rules in our main VM:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import{inject,NewInstance}from'aurelia-framework';

import{ValidationRules,ValidationController}from"aurelia-validation";

@inject(NewInstance.of(ValidationController))

exportclassApp{

message='';

firstname:string='';

lastname:string='';

constructor(private controller:ValidationController){

ValidationRules

.ensure((m:App)=>m.lastname).displayName("Surname").required()

.ensure((m:App)=>m.firstname).displayName("First name").required()

.on(this);

}

validateMe(){

this.controller

.validate()

.then(v=>{

if(v.length===0)

this.message="All is good!";

else

this.message="You have errors!";

})

}

}

Now we have to do two things in our view. We have to tell the validator which fields we would like to link to the UI for validation, and we’d like to display the list of validation errors on-screen:

XHTML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<template>

<div>

<p> Firstname: <input value.bind="firstname & validate"/></p>

<p> Lastname: <input value.bind="lastname & validate"/></p>

</div>

<button click.delegate="validateMe()">Validate</button>

<h3>Latest validation result: ${message}</h3>

<ul>

<li repeat.for="error of controller.errors">

${error.message}

</li>

</ul>

</template>

OK, so now we have an extremely unexciting form, but it’s actually pretty powerful. When the form opens the error list is empty, but as we tab over each field the validation engine applies our rules against the associated property (which it found following the Binding), but it only applied the rule when the field lost focus.
It is possible to have the validation fire on every change, on blur, or only manually, and this can be set at a ValidationController level and on a per-field basis.

So we now have everything to test and display errors.

So what if we want to have the errors displayed on a per-field basis?

Well this is highly solution-centric, as the validation system doesn’t know if you want to use Bootstrap 3.0 errors, roll-your-own, or simply change the colour of a field. Even if you simply want to change colour, what class would you like assigning to the input element?

So we now have the option of adding as many validation renders as we like. They take a really simply set of parameters, and you can perform as much DOM manipulation as you like, including making your renders conditional based on information regarding the rule or the associated input element.

Here’s a very simple one to add a class to an element, and to add a DIV element with the message: