Skip to content

Aurelia Form Validation with aurelia-validation

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Aurelia Form Validation with aurelia. We cover key concepts, practical examples, and best practices to help you master this topic.

Aurelia's validation plugin provides a declarative system for defining validation rules on your models, with automatic binding to form controls and customizable error display.

What You'll Learn

  • Installing and configuring aurelia-validation
  • Defining validation rules with decorators
  • Displaying validation errors in templates
  • Creating custom validation rules
  • Async validation for server-side checks

Why It Matters

Manual validation code is repetitive and error-prone. A declarative validation system keeps your rules close to your model definitions and automatically synchronizes error states with the UI.

Real-World Use

A user registration form with required fields, email format validation, password strength requirements, and async username availability checking.

Validation Flow

flowchart TD
    A[User Input] --> B[Binding Engine]
    B --> C[Validation Controller]
    C --> D[Rule Evaluator]
    D --> E{Valid?}
    E -->|Yes| F[Clear Errors]
    E -->|No| G[Collect Errors]
    G --> H[Update Error Renderer]
    H --> I[Show Messages in UI]
    F --> I
    style C fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px

Installing and Configuring

npm install aurelia-validation

Register the plugin:

import { PLATFORM } from 'aurelia-pal';

export function configure(aurelia) {
  aurelia.use
    .standardConfiguration()
    .plugin(PLATFORM.moduleName('aurelia-validation'));

  aurelia.start().then(() => aurelia.setRoot());
}

Defining Validation Rules with Decorators

Use the @ValidationRules decorator and ValidationRules fluent API:

import { ValidationRules, Validator } from 'aurelia-validation';

export class RegistrationForm {
  username = '';
  email = '';
  password = '';
  confirmPassword = '';

  constructor() {
    ValidationRules
      .ensure('username')
        .required().minLength(3).maxLength(20)
      .ensure('email')
        .required().email()
      .ensure('password')
        .required().minLength(8).matches(/^(?=.*[A-Z])(?=.*\d)/)
      .ensure('confirmPassword')
        .required().satisfies((value, form) => value === form.password)
      .on(this);
  }
}

Expected output: Each property has validation rules defined: username must be 3-20 characters, email must be valid, password must be 8+ chars with an uppercase letter and digit, and confirmPassword must match password.

Displaying Validation Errors in Templates

Use the validation-errors custom attribute and validate binding behavior:

<template>
  <form submit.delegate="submitForm()">
    <div class="form-group">
      <label>Username</label>
      <input type="text" value.bind="username & validate">
      <span class="error" repeat.for="error of validationController.errors"
            if.bind="error.propertyName === 'username'">
        ${error.message}
      </span>
    </div>

    <div class="form-group">
      <label>Email</label>
      <input type="email" value.bind="email & validate">
    </div>

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

Expected output: As the user types, validation errors appear live beneath each field. The & validate binding behavior triggers validation on input changes.

Using the Validation Controller

import { autoinject } from 'aurelia-framework';
import { ValidationController, ValidationControllerFactory } from 'aurelia-validation';

@autoinject
export class RegistrationForm {
  controller: ValidationController;

  constructor(controllerFactory: ValidationControllerFactory) {
    this.controller = controllerFactory.createForCurrentScope();
  }

  async submitForm(): Promise<void> {
    const result = await this.controller.validate();
    if (result.valid) {
      // Submit to server
    } else {
      // Show general error message
    }
  }
}

Expected output: Calling controller.validate() returns a ValidateResult object with a valid boolean and array of individual results.

Creating a Custom Validation Rule

import { ValidationRule } from 'aurelia-validation';

export class PhoneNumberRule implements ValidationRule {
  constructor(public message: string = 'Invalid phone number format') {}

  execute(value: any, form: any): boolean {
    if (!value || value === '') return true; // Skip empty (use required for that)
    const cleaned = value.replace(/[\s\-\(\)]/g, '');
    return /^\d{10,15}$/.test(cleaned);
  }
}

// Usage
ValidationRules
  .ensure('phone')
    .satisfiesRule(new PhoneNumberRule())
  .on(this);

Expected output: The phone field validates against the custom rule. If the cleaned value is not 10-15 digits, the error message appears.

Async Validation (Server-Side Check)

import { ValidationRules } from 'aurelia-validation';
import { ApiService } from './api-service';

export class RegistrationForm {
  constructor(private api: ApiService) {
    ValidationRules
      .ensure('username')
        .required()
        .satisfies(async (value) => {
          const available = await this.api.checkUsername(value);
          return available === true;
        }, { message: 'Username is already taken' })
      .on(this);
  }
}

Expected output: When the username field loses focus or the value changes, the validation controller sends a request to the server. While the async check is pending, the field shows a validating state. If the username is taken, the error message appears.

Common Mistakes

  1. Not registering the validation plugin - Using & validate without registering aurelia-validation throws a runtime error.

  2. Forgetting to create a ValidationController - Rules alone do nothing. You must inject a ValidationControllerFactory and create a controller.

  3. Using satisfies without a custom message - The default message "is not valid" is unhelpful. Always provide a { message } option.

  4. Async rules blocking the UI - Async validators should be debounced and cancelled when the input changes again. Use a debounce binding behavior like & debounce:300.

  5. Not clearing validation on reset - When you reset a form, call controller.reset() to clear all error messages; otherwise old errors remain visible.

Practice Questions

  1. What binding behavior triggers validation on input changes?
  2. How do you create a validation controller for a component?
  3. What method returns a promise that resolves to a ValidateResult object?
  4. How do you define a custom validation rule class?
  5. What should you call when resetting a form to clear all validation errors?

Challenge: Build a registration form with synchronous validation for required fields, email format, and password strength, plus async validation that checks username availability against a mock API endpoint.

FAQ

Can aurelia-validation validate nested objects?

Yes, use the ensure method with dot-notation paths like ensure('address.street').required() to validate nested properties.

How do I localize validation error messages?

Use the messageProvider configuration option on the validation controller to customize message generation or provide translated strings.

Does validation work with custom elements?

Yes, custom elements can participate in validation if they implement the ValidationRenderer interface or use the validate binding behavior on their internal controls.

Can I validate only a single property instead of the whole form?

Yes, call controller.validate({ object: this, propertyName: 'email' }) to validate a single property in isolation.

How do I show validation errors in a summary list at the top of the form?

Subscribe to the controller's validate event or iterate controller.errors in a repeat.for loop to render all errors in a summary block.

Mini Project

Build a user profile editor with fields for name, email, phone, website, and bio. Add synchronous validation rules for each field (required, format, length) and a custom website URL validator. Display errors inline beneath each field and in a summary panel at the top.

What's Next

Now that you can validate forms, learn how to write automated tests for your Aurelia components and services.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro