LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • 0001 to 0023
      • 0088 to 0723
      • 0901 to 0908
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page
  • Introduction
  • Installation
  • Enable and configure the plugin
  • Create a form
  • Add the validation plugin
  • Create validation rules
  • Display error messages
  • Turning input fields red on error
  • Conditional validation logic
  • Do something on successful validation
  • Conclusion

Was this helpful?

Export as PDF
  1. Aurelia Packages
  2. Validation

Validation Tutorial

Learn how to use the Aurelia Validation package with this comprehensive tutorial.

PreviousValidationNextPlugin Configuration

Last updated 1 year ago

Was this helpful?

Introduction

Aurelia provides a powerful validation library that allows you to add validation to your applications. If you are new to Aurelia, we recommend visiting the section first to familiarize yourself with the framework.

This tutorial aims to teach you all the basics of validation. Enabling it, validation rules, conditional validation and multiple objects.

While building our form application, we will cover the following:

  • How to configure validation

  • Working with built-in validation rules

  • Conditional logic (if this, then that)

  • Highlighting input fields using a custom renderer and displaying errors

  • Working with multiple objects

A working demo and code for the following tutorial can also be found .

Installation

To do this tutorial, you'll need a working Aurelia application. We highly recommend following the guide to do this. However, for this tutorial, we have a ready to go that we recommend. It will allow you to follow along and live code.

Because the validation packages do not come with Aurelia out-of-the-box, you will need to install them as detailed in the . The linked code environment already has these dependencies added for you.

Enable and configure the plugin

We need to tell Aurelia we want to use the validation package by registering it in main.ts via the register method.

import { ValidationHtmlConfiguration } from '@aurelia/validation-html';
import Aurelia from 'aurelia';

import { MyApp } from './my-app';

Aurelia
  .register(ValidationHtmlConfiguration)
  .app(MyApp)
  .start();

The ValidationHtmlConfiguration object will configure our Aurelia application to use HTML validation, and that's all we need to do to start using it.

Create a form

Because we will be using the validation package on a form (the most common scenario with validation), we will create one in my-app.html

my-app.html
<form>
  <div><input type="text" placeholder="First Name" value.bind="firstName & validate"></div>
  <br>
  <div><input type="text" placeholder="Last Name" value.bind="lastName & validate"></div>
  <br>
  <div><input type="number" placeholder="Age value.bind="age & validate"></div>
  <br>
  <div><input type="email" placeholder="Email Address" value.bind="email & validate"></div>
  <br>
  <div><input type="url" placeholder="http://google.com" value.bind="website & validate"></div>
  <br>
  <button type="button" click.trigger="add()">Add</button>
</form>

We have added a few form input elements and bound their values to class properties inside our view model. One thing to point out here is & validate which is a binding behavior that tells the validation library we want to validate these bindable values.

Add the validation plugin

We now need to include the validation plugin in our component. We'll inject it using Dependency Injection and then create rules and implement validation logic.

my-app.ts
import { newInstanceForScope, resolve } from '@aurelia/kernel';
import { IValidationRules } from '@aurelia/validation';
import { IValidationController } from '@aurelia/validation-html';

export class MyApp {
  public constructor(
    readonly validationController: IValidationController = resolve(newInstanceForScope(IValidationController)),
    readonly validationRules: IValidationRules = resolve(IValidationRules)
  ) {}
}

resolve(newInstanceForScope(IValidationController)) injects a new instance of validation controller, which is made available to the children of my-app

IValidationRules is what we will use to register our validation rules that we validate against in our views.

Create validation rules

Now we have our validation plugin added to the component, let's write validation rules.

my-app.ts

import { newInstanceForScope, resolve } from '@aurelia/kernel';
import { IValidationRules } from '@aurelia/validation';
import { IValidationController } from '@aurelia/validation-html';

export class MyApp {
  public constructor(
    private validationController: IValidationController = resolve(newInstanceForScope(IValidationController)),
    validationRules: IValidationRules = resolve(IValidationRules),
  ) {
    validationRules
      .on(this)
      .ensure('firstName')
      .required()
      .ensure('lastName')
      .required()
      .ensure('age')
      .required()
      .min(18)
      .max(110)
      .ensure('email')
      .required()
      .email()
      .ensure('website')
      .required();
  }
  public async add() {
    const result = await this.validationController.validate();
    if (result.valid) {
    } else {
      console.log(result);
    }
  }
}

By calling the validationRules API, we first must tell the plugin what we are validating. In this instance, its properties on our component class hence the on(this) part. If you wanted to validate an object called user, you would write on(this.user).

Running what we have so far will successfully validate our inputs but will provide no visual feedback.

Display error messages

We need a way to tell the user there are errors. We can get error messages from the validation controller and display them with very little code.

my-app.html
<form>
  <ul>
    <li repeat.for="error of validationController.results" if.bind="!error.valid">${error}</li>
  </ul>

  <div><input type="text" placeholder="First Name" value.bind="firstName & validate"></div>
  <br>
  <div><input type="text" placeholder="Last Name" value.bind="lastName & validate"></div>
  <br>
  <div><input type="number" placeholder="Age" value.bind="age & validate"></div>
  <br>
  <div><input type="email" placeholder="Email Address" value.bind="email & validate"></div>
  <br>
  <div><input type="url" placeholder="http://google.com" value.bind="website & validate"></div>
  <br>
  <button type="button" click.trigger="add()">Add</button>
</form>

Clicking the add button will now display some error messages. Try filling out the fields and see the error messages appear and disappear.

Turning input fields red on error

A common UI pattern when adding validation is turning the input fields red to highlight error states. While the Validation plugin provides a few ways to do this, we will leverage the validation-errors attribute.

my-app.html
<form>
  <ul>
    <li repeat.for="error of validationController.results" if.bind="!error.valid">${error}</li>
  </ul>
  <div validation-errors.from-view="firstNameErrors">
  <input class="${firstNameErrors.length ? 'error' : ''}" type="text" placeholder="First Name" value.bind="firstName & validate"></div>
  <br>
  <div validation-errors.from-view="lastNameErrors">
  <input class="${lastNameErrors.length ? 'error' : ''}" type="text" placeholder="Last Name" value.bind="lastName & validate"></div>
  <br>
  <div validation-errors.from-view="ageErrors">
  <input class="${ageErrors.length ? 'error' : ''}" type="number" placeholder="Age" value.bind="age & validate"></div>
  <br>
  <div validation-errors.from-view="emailErrors">
  <input class="${emailErrors.length ? 'error' : ''}" type="email" placeholder="Email Address" value.bind="email & validate"></div>
  <br>
  <div validation-errors.from-view="websiteErrors">
  <input class="${websiteErrors.length ? 'error' : ''}" type="url" placeholder="http://google.com" value.bind="website & validate"></div>
  <br>
  <button type="button" click.trigger="add()">Add</button>
</form>

We place the validation-errors attribute on the surrounding DIV element, then use the pattern of propertyNameErrors where propertyName our property's name is and Errors is the suffix that Aurelia sees as an error pointer.

Inside my-app.css add a CSS class called error

my-app.css
.error {
  border: 1px solid #ff0000;
  color: #ff0000;
}

Conditional validation logic

We could end things here. Our form has validation, and it shows error messages and styling. However, we are going to implement some conditional logic. Validation rules get called when certain values meet criteria (like radio button selections).

my-app.html
<form>
  <ul>
    <li repeat.for="error of validationController.results" if.bind="!error.valid">${error}</li>
  </ul>

  <div validation-errors.from-view="firstNameErrors">
  <input class="${firstNameErrors.length ? 'error' : ''}" type="text" placeholder="First Name" value.bind="firstName & validate"></div>
  <br>
  <div validation-errors.from-view="lastNameErrors">
  <input class="${lastNameErrors.length ? 'error' : ''}" type="text" placeholder="Last Name" value.bind="lastName & validate"></div>
  <br>
  <div validation-errors.from-view="ageErrors">
  <input class="${ageErrors.length ? 'error' : ''}" type="number" placeholder="Age" value.bind="age & validate"></div>
  <br>
  <div validation-errors.from-view="typeErrors">
    <label class="${typeErrors.length ? 'error' : ''}">
      <input type="radio" name="type" checked.bind="type" value="user" />
      User
    </label>
    <label class="${typeErrors.length ? 'error' : ''}">
      <input type="radio" name="type" checked.bind="type" value="customer" />
      Customer
    </label>
    <input type="hidden" value.bind="type & validate" />
  </div>
  <br>
  <div validation-errors.from-view="emailErrors">
  <input class="${emailErrors.length ? 'error' : ''}" type="email" placeholder="Email Address" value.bind="email & validate"></div>
  <br>
  <div validation-errors.from-view="websiteErrors" if.bind="type == 'user'">
  <input class="${websiteErrors.length ? 'error' : ''}" type="url" placeholder="http://google.com" value.bind="website & validate"></div>
  <br>
  <button type="button" click.trigger="add()">Add</button>
</form>

We have highlighted the changes to our form. We have added in a DIV with two radio inputs and a hidden input. The hidden input allows us to validate the value or the validator will see each radio input as a separate thing to validate.

We also add an if.bind to our website field as we are making the URL only mandatory for new users, not new customers.

my-app.ts

import { newInstanceForScope, resolve } from '@aurelia/kernel';
import { IValidationRules } from '@aurelia/validation';
import { IValidationController } from '@aurelia/validation-html';

export class MyApp {
  public constructor(
    private validationController: IValidationController = resolve(newInstanceForScope(IValidationController)),
    validationRules: IValidationRules = resolve(IValidationRules),
  ) {
    validationRules
      .on(this)
      .ensure('firstName')
      .required()
      .ensure('lastName')
      .required()
      .ensure('age')
      .required()
      .min(18)
      .max(110)
      .ensure('type')
      .required()
      .ensure('email')
      .required()
      .email()
      .ensure('website')
      .required()
        .when(obj => obj.type === 'user')
        .withMessage(`Website is required when creating a new user.`)
  }

  public async add() {
    const result = await this.validationController.validate();
    if (result.valid) {
    } else {
      console.log(result);
    }
  }
}

We make a slight change to our website rules by using .when to introduce a condition to our validation. The obj The object itself is returned, allowing us to inspect other object property values.

  • When the type value is user

  • Make the website property mandatory

  • Use withMessage to create a custom validation message

Do something on successful validation

We already have the code for this part, but we must talk about it. In our my-app.ts file, we created a method called add which calls our validation controller.

my-app.ts
public async add() {
  const result = await this.validationController.validate();

  if (result.valid) {
    // Make an API call or do something here when validation passes
  } else {
    console.log(result);
  }
}

Calling the validate method on the validation controller, which returns a promise, allows us to check the valid property to determine if validation was successful or not. The valid value will be true if all validation rules pass and false if one or more do not. In the case of a form where we create users, we would probably call an API or something to save.

The cool thing about this is that we also get back all the validation rules (the ones that pass and fail) on the results property. This allows us to do things in our code without relying on the view. An example might be to display a toast notification with an error message for one or more errors.

Conclusion

While we only scratched the surface of what the validator can do, we have covered all of the essential aspects of validation. We highly recommend reading over the validation documentation to better understand the validation library.

The code above can be found in a working demo application .

Getting Started
here
Quick Start
starter Aurelia 2 application
Validation section
here
LogoAurelia Validation Demo - StackBlitzStackBlitz