# Validation Controller

So far, the functionalities of the `@aurelia/validation` have been discussed. The part regarding the integration with a view has been kept out of the discussion so far. This section starts addressing that.

The validation controller is the implementation of `IValidationController` interface. It acts as a bridge between the validator and the other related components, such as view, binding, and subscribers. The capabilities of the validation controller are discussed below.

## Injecting a controller instance

An instance of the validation controller can be injected using `resolve(newInstanceForScope(IValidationController))`, and `resolve(IValidationController)`. The `newInstanceForScope(IValidationController)` resolver creates a new instance of the validation controller and registers the instance with the dependency injection container. This same instance can later be made available to the child components using `resolve(IValidationController)`.

```typescript
// parent-ce.ts
import { customElement } from '@aurelia/runtime-html';
import { newInstanceForScope, resolve } from '@aurelia/kernel';
import { IValidationController } from '@aurelia/validation-html';

@customElement({name:'parent-ce', template:`<child-ce></child-ce>`})
export class ParentCe {
  // new instance of validation controller; let us name it c1
  private controller = resolve(newInstanceForScope(IValidationController));
}

// child-ce.ts
import { resolve } from '@aurelia/kernel';
import { IValidationController } from '@aurelia/validation-html';

export class ChildCe {
  // the c1 instance is injected here
  private controller = resolve(IValidationController);
}
```

{% hint style="info" %}
The design decision is made keeping the following frequent use case in mind. The manual/final validation happens in the "root"/"parent" component/custom element. The child components, such as other custom elements, define the necessary validation rules at the custom element level, as well as uses the `validate` binding behavior to mark the validation targets in the view/markup. This helps show the validation messages near the validation targets.\
\
Creating a new instance of the validation controller and registering the instance with the dependency injection container makes the same instance available to the child components level. The instance can then be used for registering the validation targets (see [`validate` binding behavior](/aurelia-packages/validation/validate-binding-behavior.md)), which makes it possible to execute all the validation rules defined in the children with a single instance of the controller.
{% endhint %}

A new instance of validation controller can always be injected using the `newInstanceOf(IValidationController)` resolver. See this action in the demo below.

{% embed url="<https://stackblitz.com/edit/au2-validation-injecting-validation-controller>" %}

## `validate` and `reset`

The `validate` method can be used to explicitly/manually perform the validation. The usage examples are as follows.

```typescript
// validate all registered objects and bindings.
await validationController.validate();

// validate specific instruction
await validationController.validate(new ValidateInstruction(person));
await validationController.validate(new ValidateInstruction(person, 'name'));
```

> This method is in essence similar to the `validate` method in validator. However, there are some differences. If the method is called with an instruction, the instruction is executed. Otherwise all the [registered objects](#addobject-and-removeobject), as well as the [registered bindings](/aurelia-packages/validation/validate-binding-behavior.md) are validated. After the validation, all the [registered subscribers](#addsubscriber-and-removesubscriber) are notified of the change. Refer to the workflow diagram in the [architecture overview](/aurelia-packages/validation/architecture.md) to understand it better. To know more about `ValidateInstruction` refer [the dedicated section](/aurelia-packages/validation/defining-rules.md#validateinstruction).

The `reset` method on the other hand removes the errors from the validation controller. It also has an optional argument of type `ValidateInstruction` which when provided instructs the controller to remove errors for specific object, and/or properties. Note that other properties of the instruction object has no effect on resetting the errors.

{% embed url="<https://stackblitz.com/edit/au2-validation-validationcontroller-validate-reset?ctl=1>" %}

### `revalidateErrors`

With the `revalidateErrors` method, verifying whether the current errors are still there is possible. It does not validate all objects and bindings, as it is done in `validate` method. It is useful when you don't want to get a new set of errors and rather check on the current status of the existing set of errors.

```typescript
await validationController.revalidateErrors();
```

{% embed url="<https://stackblitz.com/edit/au2-validation-validationcontroller-revalidateerrors?ctl=1>" %}

## `addObject` and `removeObject`

The method `addObject` registers an object explicitly to the validation controller. The validation controller automatically validates the object every time the `validate` method is called. This is useful when you can validate some object in your view model that does not have any direct reference to the view.

The object can be unregistered by calling the `removeObject` method. This also removes the associated errors of the object.

```typescript
// add object
validationController.addObject(person);

// remove object
validationController.removeObject(person);
```

{% embed url="<https://stackblitz.com/edit/au2-validation-validationcontroller-addobject-removeobject?ctl=1>" %}

## `addError` and `removeError`

Use the `addError` method to manually add an error to the controller. The signature of this method is as follows.

```typescript
addError(message: string, object: any, propertyName?: string): ValidationResult;
```

Note that this method returns an instance of `ValidationResult` which later can be used with `removeError` to clear the error.

```typescript
// add error
const result= validationController.addError("Some critical error", person);

// remove error
validationController.removeError(result);
```

Note that the errors added by the `addError` method, never gets revalidated when `revalidateErrors` is called. If the error needs to be removed, it must be done using `removeError` method.

{% embed url="<https://stackblitz.com/edit/au2-validation-validationcontroller-adderror-removeerror?ctl=1>" %}

## `addSubscriber` and `removeSubscriber`

The subscribers can be added or removed using `addSubscriber` and `removeSubscriber` methods respectively. Whenever the validation controller performs validation or resets errors, the registered subscribers are notified of the change in validation results. To unsubscribe from the validation results notification, the subscriber needs to be removed.

The subscriber interface is rather simple, consisting of only one method.

```typescript
interface ValidationResultsSubscriber {
  handleValidationEvent(event: ValidationEvent): void;
}
```

The notification event data looks loosely like the following.

```typescript
class ValidationEvent {
  public kind: 'validate' | 'reset';
  public addedResults: ValidationResultTarget[];
  public removedResults: ValidationResultTarget[];
}

class ValidationResultTarget {
  public result: ValidationResult;
  public targets: Element[];
}

class ValidationResult<TRule extends IValidationRule = IValidationRule> {
    public valid: boolean;
    public message: string | undefined;
    public propertyName: string | undefined;
    public object: any;
    public rule: TRule | undefined;
    public propertyRule: PropertyRule | undefined;
    // `true` if the validation result is added manually.
    public isManual: boolean = false;
}
```

What the subscribers do with the event data depends on the subscribers. An obvious use case is to present the errors to the end users. In fact, the out-of-the-box subscribers are used for that purpose only. Below is one example of how you can create a custom subscriber.

{% embed url="<https://stackblitz.com/edit/au2-validation-validationcontroller-add-remove-subscriber>" %}

## Advanced Controller Methods

The validation controller provides additional methods for fine-grained control over validation in advanced scenarios.

### `validateBinding`

Validates a specific binding that was registered using the `& validate` binding behavior. This method is useful when you want to trigger validation for a single input field programmatically.

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

export class AdvancedForm {
  private validationController = resolve(newInstanceForScope(IValidationController));

  public async validateSpecificField(binding: any): Promise<void> {
    // Validate only this specific binding
    await this.validationController.validateBinding(binding);
  }
}
```

**Parameters:**

* `binding`: The binding instance (typically a PropertyBinding with the validate binding behavior)

**Behavior:**

* Validates only the specified binding, not all registered bindings
* Uses any rules that were bound to the binding through the validate binding behavior
* Notifies subscribers of validation results
* Does nothing if the binding is not bound or not registered with the controller

**Note:** This method is typically used internally by the validation system. Most applications won't need to call it directly, as the `& validate` binding behavior handles validation automatically based on the configured trigger.

### `resetBinding`

Resets validation results for a specific binding. This clears any validation errors associated with that binding.

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

export class AdvancedForm {
  private validationController = resolve(newInstanceForScope(IValidationController));

  public resetSpecificField(binding: any): void {
    // Clear errors for this specific binding
    this.validationController.resetBinding(binding);
  }
}
```

**Parameters:**

* `binding`: The binding instance to reset

**Behavior:**

* Removes all validation results associated with the binding
* Clears the binding's cached property information
* Notifies subscribers that errors were removed
* Does nothing if the binding is not registered with the controller

**Use cases:**

* Clearing validation errors when a user starts editing a field again
* Resetting validation state when switching between form modes
* Implementing custom validation triggers

### Complete Example: Custom Validation Control

Here's an example showing how to use these advanced methods to implement custom validation behavior:

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

export class CustomValidationForm {
  private validationController = resolve(newInstanceForScope(IValidationController));
  private validationRules = resolve(IValidationRules);

  public username: string = '';
  public email: string = '';
  public showValidation: boolean = false;

  private usernameBinding: any;
  private emailBinding: any;

  public constructor() {
    this.validationRules
      .on(this)
      .ensure('username')
      .required()
      .minLength(3)
      .ensure('email')
      .required()
      .email();
  }

  // Store binding references when they're created
  public registerBinding(propertyName: string, binding: any): void {
    if (propertyName === 'username') {
      this.usernameBinding = binding;
    } else if (propertyName === 'email') {
      this.emailBinding = binding;
    }
  }

  // Validate individual fields on demand
  public async validateUsername(): Promise<void> {
    if (this.usernameBinding) {
      await this.validationController.validateBinding(this.usernameBinding);
    }
  }

  public async validateEmail(): Promise<void> {
    if (this.emailBinding) {
      await this.validationController.validateBinding(this.emailBinding);
    }
  }

  // Reset individual fields
  public resetUsername(): void {
    if (this.usernameBinding) {
      this.validationController.resetBinding(this.usernameBinding);
    }
  }

  public resetEmail(): void {
    if (this.emailBinding) {
      this.validationController.resetBinding(this.emailBinding);
    }
  }

  // Progressive validation: validate fields as user progresses
  public async onUsernameBlur(): Promise<void> {
    await this.validateUsername();
  }

  public async onEmailBlur(): Promise<void> {
    await this.validateEmail();
  }

  // Clear validation when user focuses field again
  public onUsernameFocus(): void {
    this.resetUsername();
  }

  public onEmailFocus(): void {
    this.resetEmail();
  }

  public async submit(): Promise<void> {
    const result = await this.validationController.validate();

    if (result.valid) {
      // Process form
      console.log('Form is valid!');
    } else {
      this.showValidation = true;
    }
  }
}
```

### Important Considerations

* **Internal Methods**: `validateBinding` and `resetBinding` are primarily used internally by the validation system. The validate binding behavior handles calling these methods automatically.
* **Binding References**: To use these methods, you need a reference to the actual binding instance. This is not commonly available in typical application code.
* **Validation Triggers**: For most scenarios, configuring the validation trigger (via `ValidationTrigger` enum) is a better approach than manually calling these methods.
* **Controller State**: Both methods respect the controller's current state and subscriber notifications, ensuring the validation system remains consistent.

### When to Use Advanced Methods

Consider using `validateBinding` and `resetBinding` when:

1. **Custom Validation UX**: Implementing validation behavior that doesn't fit standard triggers
2. **Progressive Enhancement**: Validating fields in a specific sequence or order
3. **Conditional Validation**: Showing/hiding validation based on complex application state
4. **Integration**: Integrating Aurelia validation with third-party form libraries

For most applications, the standard validation controller methods (`validate`, `reset`, `revalidateErrors`) combined with the `& validate` binding behavior will be sufficient.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aurelia.io/aurelia-packages/validation/validation-controller.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
