Part 2: Custom Validation, Enhanced Interface, Error Handling, and Dynamic Data Binding
Tags: angular, dynamicforms, forms, configuration
Published: 2023-08-13 17:57:00

---
title: Part 2: Custom Validation, Enhanced Interface, Error Handling, and Dynamic Data Binding
published: true
description: In this article, we'll enhance dynamic forms by adding custom validation errors, extending the `DynamicFormFieldModel` interface, introducing an error component, and demonstrating dynamic data binding. These advanced features improve user feedback, form flexibility, and interactivity. We provide code examples and a live demo on StackBlitz for hands-on learning. Stay tuned for the next installment, where we explore advanced form handling techniques. Whether you're new to dynamic forms or seeking to expand your skills, this series has valuable insights to offer. Let's make your forms truly dynamic! 🚀
tags: Angular, DynamicForms, Forms, Configuration
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9fpyamjt1za8ivpj51m.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-08-06 07:21 +0000
---
## Introduction
 
Continuing from [Part 1: Create Configuration Based Dynamic Forms in Angular](https://dev.to/loukaskotas/part-1-create-configuration-based-dynamic-forms-in-angular-7og), where we explored dynamic forms in Angular via a configuration-driven approach, this article advances further. Angular's versatility for building forms is undeniable, yet default validation messages sometimes lack. Here, we delve into **custom validation errors**, in order to provide more meaningful and user-centric feedback in Angular forms, enhancing the clarity and relevance of error messages, leading to a better user experience. By the end of this article, you'll be able to:

- Implement a dedicated error component for streamlined error handling in dynamic forms.
- Extend the capabilities of the `DynamicFormFieldModel` interface with custom validators.
- Achieve **dynamic data binding** using template references and the `@ViewChildren` decorator.
- Enhance the user experience of your Angular forms with more meaningful error messages and improved interactivity.

## Code Example 
Find a live code example on StackBlitz [here](https://stackblitz.com/edit/stackblitz-starters-4tdlmu?file=src%2Fmain.ts). Feel free to explore the code and experiment with dynamic forms yourself!

Let's dive in! 🚀


## Adding Custom Validation Errors

To begin, we'll empower the `DynamicFormFieldModel` by introducing the `ValidatorError` interface. This interface allows us to define custom validation errors, providing more meaningful feedback to users. We use the `ErrorAssosiation` enum to associate each custom error with a specific validation scenario, making it easy to identify the error type triggered by each validation. The `errorMessage` field within the `ValidatorError` interface serves as the custom error message for each validation error. Furthermore, the `errorAssosiation` field within this interface serves as a critical linkage between the error object and a specific reactive form error identifier. This linkage ensures precise error identification, facilitating a sophisticated error handling mechanism, and fostering effective communication with users.

```typescript
// ValidatorError interface and ErrorAssosiation enum
export interface ValidatorError {
  validator: ValidatorFn;
  errorMessage?: string;
  errorAssosiation: ErrorAssosiation;
}

export enum ErrorAssosiation {
  // Native Validation Error Identifiers
  REQUIRED = 'required',
  MINLENGTH = 'minlength',
  MAXLENGTH = 'maxlength',
  EMAIL = 'email',
  MIN = 'min',
  MAX = 'max',
  PATTERN = 'pattern',
  REQUIREDTRUE = 'requiredtrue',
  NULLVALIDATOR = 'nullvalidator',
  // Custom Validation Error Identifier
  UPPERCASE = 'uppercaseLetter',
}
```

## Enhancing the Interface for Custom Validators

With the `ValidatorError` interface in place, we upgrade the `DynamicFormFieldModel` interface by replacing the traditional `validators` field with this new interface. This enhancement enables us to attach custom validation errors to form fields, resulting in more relevant and user-focused error messages. Now, each validator in the configuration can have a corresponding error message, greatly improving the overall user experience.

```typescript
// Enhanced DynamicFormFieldModel with ValidatorError interface
export interface DynamicFormFieldModel {
  id: string;
  label: string;
  type: DynamicFormFieldModelType;
  selectMenuOptions?: SelectMenuOption[];
  value?: string | SelectMenuOption[]; // default value of the dynamic field
  validators?: ValidatorError[];
}

// Example configuration with custom validation errors
...
{
      id: 'favoriteMovies',
      label: 'Favorite Movies',
      type: 'select',
      selectMenuOptions: [],
      validators: [
        {
          validator: Validators.required,
          errorMessage: 'Field is required',
          errorAssosiation: ErrorAssosiation.REQUIRED,
        },
        {
          validator: Validators.minLength(3),
          errorMessage: 'You need to select at least 3 movies',
          errorAssosiation: ErrorAssosiation.MINLENGTH,
        },
      ],
    },
...
```

## Creating a Separate Error Component

While our enhancements have significantly improved the form's flexibility, we encounter an issue with code repetition in the template when displaying error messages. To address this, we introduce a dedicated component: `ErrorMessageComponent`. This component handles the display of error messages below each dynamic form field, ensuring a cleaner and more organized user interface. By utilizing the `ValidatorError` associations, we can present the correct custom error message for each validation error.

```html
<!-- error-message.component.html -->
<span *ngIf="form.get(formItem.id)!.dirty">{{ getValidationErrorMessage(formItem) }}</span>
```

```typescript
// error-message.component.ts
  @Input() formItem!: DynamicFormFieldModel;
  form!: FormGroup;

  constructor(private rootFormGroup: FormGroupDirective) {
    this.form = this.rootFormGroup.control;
  }

  getValidationErrorMessage(field: DynamicFormFieldModel): string {
    const control = this.form.get(field.id);
    const errorKey = Object.keys(control!.errors || {})[0];

    // Here we assosiate the form control error with the custom error
    const customError = field.validators?.find(
      (validatorError) => validatorError.errorAssosiation === errorKey
    );

    return customError?.errorMessage || '';
  }
```

## Achieving Dynamic Data Binding

In the final section, we explore the dynamic data binding capabilities of our enhanced dynamic forms. To accomplish this, we use **template references** (#) to identify each dynamic form field. We leverage the `@ViewChildren` decorator to access these fields, and in the `ngAfterViewInit` lifecycle hook, we fetch data from a service. We demonstrate this with an example of asynchronously fetching a user's favorite movies and passing the data to the relevant dynamic form field. This dynamic data binding technique enhances the form's adaptability and user interaction.

```typescript
// ngAfterViewInit in the component
@ViewChildren('dynamicFormField')
dynamicFormFieldContainers!: QueryList<ElementRef>;

ngAfterViewInit() {
  const favoriteMoviesId = 'favoriteMovies';
  const favoriteMovies = this.getFieldContainer(favoriteMoviesId) as any;
  this.mainService.getFavoriteMovies().subscribe((movies) => {
    favoriteMovies['formItem']['selectMenuOptions'] = movies;
  });
}

getFieldContainer(id: string): ElementRef {
  return this.dynamicFormFieldContainers.filter((d: any) => d['formItem']['id'] === id)[0];
}
```

```typescript
// Service for fetching movie data
@Injectable({ providedIn: 'root' })
export class MainService {
  constructor() {}

  getFavoriteMovies(): Observable<SelectMenuOption[]> {
    return of([
      {
        label: 'Star Wars',
        value: 'e7a9c049-1a85-4c5a-b755-1b0a5c4394a3',
      },
      {
        label: 'Lord of The Rings',
        value: 'd624c916-d6f7-487f-8b16-87a034de3e34',
      },
      {
        label: 'Shutter Island',
        value: 'cf7cb566-6d2b-4b6b-96a3-ae9fba2a47af',
      },
      {
        label: 'The Godfather',
        value: 'b19b3edc-32da-4684-a10f-6e019df00d9e',
      },
    ]);
  }
}
```

## Summary and Next Steps 

In this article, we've covered essential aspects of creating powerful dynamic forms in Angular. We added custom validation errors, enhanced the `DynamicFormFieldModel` interface, introduced a separate error component for improved error handling, and achieved dynamic data binding. These enhancements significantly enhance the user experience and form flexibility.

**Stay tuned** for the next article, where we'll take our dynamic forms to the next level. We'll delve deeper into advanced form handling techniques and the tools to create even more dynamic and interactive Angular forms. Exciting developments are on the horizon, so keep an eye out for the upcoming part! 🚀