# Edition validation

## Live example

{% hint style="success" %}
[Edition validation](https://demos.dikesoft.com/dk-grid/editing/edition-validation) live example.
{% endhint %}

## Validator functions

Since the DikeGrid base the edition module in the [Angular's Reactive Forms](https://angular.io/guide/reactive-forms) model, you add validator functions to the columns definition.

{% hint style="info" %}
You can use the pre-built Angular's validator functions or define custom validator functions.
{% endhint %}

Let us begin defining some columns as **editable** with validators:

{% tabs %}
{% tab title="edition-validation.component.html" %}

```markup
<dike-grid id="grid-edition-validation" height="600px" #grid="dkgGrid">
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350">
    </dike-grid-column>

    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            editable
            [editionSettings]="nameEditionSettings">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            editable
            [editionSettings]="lastNameEditionSettings">
        </dike-grid-column>
    </dike-grid-column>

    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="110"
        editable
        [editionSettings]="genderEditionSettings">
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="100"
        editable
        [editionSettings]="ageEditionSettings">
    </dike-grid-column>

    <dike-grid-column
        fieldName="email"
        headerText="Email"
        dataType="Text"
        width="300"
        editable
        [editionTemplate]="emailEdition"
        [editionSettings]="emailEditionSettings">
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        editable
        [editionSettings]="hireDateEditionSettings">
    </dike-grid-column>
</dike-grid>
```

{% endtab %}

{% tab title="edition-validation.component.ts" %}

```typescript
@Component({
  selector: 'edition-validation',
  templateUrl: './edition-validation.component.html',
  styleUrls: ['./edition-validation.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionValidationComponent implements OnInit, OnDestroy {
  //...
  
  nameEditionSettings: EditionFieldSettings;
  lastNameEditionSettings: EditionFieldSettings;
  genderEditionSettings: EditionFieldSettings;
  ageEditionSettings: EditionFieldSettings;
  emailEditionSettings: EditionFieldSettings;
  hireDateEditionSettings: EditionFieldSettings;
  
  ngOnInit(): void {
    // ...

    // Define the column edition settings:
    this.nameEditionSettings = {
      required: true,
      validators: [ Validators.maxLength(12) ]
    };

    this.lastNameEditionSettings = {
      required: true,
      validators: [ Validators.maxLength(15) ]
    };

    this.genderEditionSettings = {
      required: true,
      options: [ { label: 'M', value: 'male' }, { label: 'F', value: 'female' } ]
    };

    this.ageEditionSettings = {
      required: true,
      validators: [ Validators.min(22), Validators.max(60) ]
    };

    this.emailEditionSettings = { required: true };
    this.hireDateEditionSettings = { required: true };
  }
}
```

{% endtab %}
{% endtabs %}

We have defined the following features:

1. Firstly, all the columns are **required**.
2. The **Name** column maximum length is 12.
3. The **Surname** column maximum length is 15.
4. The **Age** column only accepts values from 22 and 60.

We intentionally left the **Email** and **Hire Date** columns with no validators because we set them using the **API**.

{% hint style="info" %}
Please, go to the live example and test the validators we have set.
{% endhint %}

## Conveying error messages

When a field value does not meet the validator criteria, the DikeGrid displays a **red bar** at the **left** of the **field**.

{% hint style="success" %}
You click on the red bar to see the specific validation message.
{% endhint %}

![Conveying error messages](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FjkRhuT7TiYvyVQlCDVkh%2Fedition-validation-convey-errors.png?alt=media\&token=f0917e76-a624-4452-af2d-a25d96c85b5a)

### Standard validation messages

We have seen how to display the validation message specific to a field.

The DikeGrid offers default validation messages when validators throw an error.

{% hint style="success" %}
To see the complete list of validation messages, see the [<mark style="color:green;">`DikeStandardErrorMessage`</mark>](https://docs.dikesoft.com/reference/classes/editing#dikestandarderrormessage) class definition.
{% endhint %}

### Custom validation messages

You can provide custom validation messages by providing an **Injection Token** or at the **column** level.

#### Messages by providing an Injection Token

When you modify or add validation messages by providing an Injection Token, you affect all your DikeGrid instances that live under the place you give your Injection Token.

Let us overwrite the required validator.

{% code title="editing.module.ts" %}

```typescript
@NgModule({
  providers: [
    { provide: CUSTOM_EDITION_ERROR_MESSAGES,
      useFactory: (): CustomErrorMessage =>
        new CustomErrorMessage()
            .addMessage(ErrorType.REQUIRED, 'You can not leave the field empty.')
    }
  ]
})
export class EditingModule { }
```

{% endcode %}

As you can see, all the columns show the required validator message defined in the module.

![Custom messages at the module level](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FbWIWG1eJorZHVyBczDmY%2Fedition-validation-module-custom-message.png?alt=media\&token=228c46da-4b45-4962-ac42-ff598d3c0268)

#### Messages at the column level

When you want to be more specific in the messages you want to convey, you can define those messages at the column level.

In the following code snippet, we overwrite several validators' messages.

{% code title="edition-validation.component.ts" %}

```typescript
ngOnInit(): void {
  // ...

  // Define the column edition settings:
  this.nameEditionSettings = {
    required: true,
    validators: [ Validators.maxLength(12) ],
    errorMessages: new CustomErrorMessage()
      .addMessage(ErrorType.MAX_LENGTH, 'The Name column maximum length must be 12.')
  };

  this.lastNameEditionSettings = {
    required: true,
    validators: [ Validators.maxLength(15) ],
    errorMessages: new CustomErrorMessage()
      .addMessage(ErrorType.MAX_LENGTH, 'The Surname column maximum length must be 15.')
  };

  this.genderEditionSettings = {
    required: true,
    options: [ { label: 'M', value: 'male' }, { label: 'F', value: 'female' } ]
  };

  this.ageEditionSettings = {
    required: true,
    validators: [ Validators.min(22), Validators.max(60) ],
    errorMessages: new CustomErrorMessage()
      .addMessage(ErrorType.REQUIRED, 'You must enter the age of the employee.')
      .addMessage(ErrorType.MIN, 'The minimum age to be hired must be 22 years old.')
      .addMessage(ErrorType.MAX, 'The maximum age to be hired must be 60 years old.')
  };

  this.emailEditionSettings = { required: true };
  this.hireDateEditionSettings = { required: true };
}
```

{% endcode %}

When running the live example, pay attention to the **required** validator for the **Age** column.

![Custom messages at the column level](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FiBqFTbuPgdn3o6DZMhkx%2Fedition-validation-column-custom-message.png?alt=media\&token=b47d15fc-8acc-45a0-8d9a-6e47eb5dfa74)

The **column leve**l definition has the **highest** precedence despite overwriting the required validator at the module level. Therefore, the DikeGrid shows the **Age** column required-validator message.

## Custom validators

If the pre-built Angular validators are not enough for your use cases, you can create your custom validators.

{% hint style="info" %}
See the official [Angular docs](https://angular.io/guide/form-validation#defining-custom-validators) for further details on custom validators.
{% endhint %}

See the following code snippet.

{% tabs %}
{% tab title="edition-validation.component.ts" %}

```typescript
ngOnInit(): void {
  //...
  
  // Define the column edition settings:
  this.nameEditionSettings = {
    required: true,
    validators: [ Validators.maxLength(12), forbiddenNameValidator(/erick/i) ],
    errorMessages: new CustomErrorMessage()
      .addMessage(ErrorType.MAX_LENGTH, 'The Name column maximum length must be 12.')
      .addMessage('forbiddenName', 'The Name column can not contain the erick string.')
  };
  
  //...
}
```

{% endtab %}

{% tab title="custom-validator.ts" %}

```typescript
export const forbiddenNameValidator = (nameRe: RegExp): ValidatorFn =>
    (control: AbstractControl): ValidationErrors | null => nameRe.test(control.value) ? { forbiddenName: true } : null;
```

{% endtab %}
{% endtabs %}

We have taken the custom validator **forbiddenNameValidator** from the Angular docs and attached it to the **Name** column. The custom validator does not allow the user to type a name that contains the <mark style="color:red;">`erick`</mark> string.

We have attached a custom message to the **forbiddenNameValidator** validator as well.

![Defining a custom validator](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FNdvEQFwambkmdio7xWZn%2Fedition-validation-custom-validator.png?alt=media\&token=22f74d7d-5e35-4334-9d99-96c5d4d34d3c)

## Cross-field validation

When defining columns, the DikeGrid add them to the **center panel** if you do not specify a different panel for each column. Therefore, a DikeGrid instance could have **one** panel (center panel) or **three** (left, center, and right panels).

When you allow the user to edit rows, the DikeGrid creates one **FormGroup** for the entire **row**, one **FormGroup** for **each panel**, and one **FormControl** for **each column** field.

Having said how the DikeGrid organizes the FormControl's and the FormGroup's, you can add **cross-validation** on the **row-level FormGroup**, the outermost FormGroup.

Let us define a custom cross-validator.

{% tabs %}
{% tab title="edition-validation.component.html" %}

```markup
<dike-grid id="grid-edition-validation" height="600px" #grid="dkgGrid"
    [gridEditionSettings]="gridEditionSettings">
</dike-grid>
```

{% endtab %}

{% tab title="edition-validation.component.ts" %}

```typescript
@Component({
  selector: 'edition-validation',
  templateUrl: './edition-validation.component.html',
  styleUrls: ['./edition-validation.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionValidationComponent implements OnInit, OnDestroy {
  //...
  
  gridEditionSettings: DikeGridEditionSettings<Employee>;
  
  ngOnInit(): void {
    //...
    
    this.gridEditionSettings = {
      crossFieldValidation: {
        customValidator: menAgeCrossValidation,
        errorMessages: new CustomErrorMessage()
          .addMessage('menAge', 'The minimum age for men is 36 years old.')
        }
    };
  }
}
```

{% endtab %}

{% tab title="cross-validator.ts" %}

```typescript
// This function validates that men must be older than 35 years old:
export const menAgeCrossValidation = (columnDef: DikeGridColumnDef<Employee>): ValidatorFn => {

    // Get the desired columns:
    const genderColumn = columnDef.findColumn(column => column.fieldName === 'gender');
    const ageColumn = columnDef.findColumn(column => column.fieldName === 'age');

    // This validator function is attached to the row's FormGroup:
    return (rowGroup: AbstractControl): ValidationErrors | null => {
        // If the columns can not be found, there is no error:
        if (!genderColumn || !ageColumn) {
            return null;
        }

        // Get the controls from the corresponding FormGroup.
        // The slotId property identifies each column control inside the corresponding FormGroup:
        const genderControl = rowGroup.get(genderColumn.panel)?.get(genderColumn.slotId);
        const ageControl = rowGroup.get(ageColumn.panel)?.get(ageColumn.slotId);

        // If the controls can not be found, there is no error:
        if (!genderControl || !ageControl) {
            return null;
        }

        return genderControl.value as string === 'male' && ageControl.value as number <= 35 ? { menAge: true } : null;
    };
};
```

{% endtab %}
{% endtabs %}

In the previous code snippet:

1. We have defined a custom validator but following a specific signature that receives the related <mark style="color:green;">`DikeGridColumnDef`</mark> instance.
2. We have also created the corresponding validator message.
3. Then, we have added the cross-validator to the edition settings at the level of the DikeGrid instance.

{% hint style="success" %}
We recommend seeing the definition of the [<mark style="color:green;">`CrossFieldValidationSettings`</mark>](https://docs.dikesoft.com/reference/interfaces/editing#crossfieldvalidationsettings-less-than-t-greater-than) and [<mark style="color:green;">`DikeGridEditionSettings`</mark>](https://docs.dikesoft.com/reference/interfaces/editing#dikegrideditionsettings-less-than-t-greater-than) interfaces.
{% endhint %}

![Deining a cross-field validator](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FcHu6rAO5j1dG3YQxvWp5%2Fedition-validation-cross-validator.png?alt=media\&token=c7f0820c-de2b-46ff-8c99-3c3ec05c2cd8)

{% hint style="info" %}
When a cross-field validator throws an error, the DikeGrid displays the **red bar** at the left of the **pencil icon**.
{% endhint %}

## Assigning validators using the API

If you want to reconfigure validators functions at runtime, you can use the **column API** to achieve it.

Before using the API, we have to retrieve the DikeGrid instance from the component's view.

{% tabs %}
{% tab title="edition-validation.component.html" %}

```markup
<dike-grid id="grid-edition-validation" height="600px" #grid="dkgGrid">
</dike-grid>
```

{% endtab %}

{% tab title="edition-validation.component.ts" %}

```typescript
@Component({
  selector: 'edition-validation',
  templateUrl: './edition-validation.component.html',
  styleUrls: ['./edition-validation.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionValidationComponent implements OnInit, OnDestroy {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;
  
  //...
}
```

{% endtab %}
{% endtabs %}

### DikeGrid Column API

You can use the following method to assign validators at runtime.

| Method                | Description                                                           |
| --------------------- | --------------------------------------------------------------------- |
| `setColumnEditable()` | Use this method to set validators through the edition settings param. |

{% hint style="success" %}
For further details, see the [<mark style="color:green;">`DikeGridColumnDef`</mark>](https://docs.dikesoft.com/reference/dkgrid-api/dkgridcolumndef#methods) definition.
{% endhint %}

Consider the following additions to the UI:

{% tabs %}
{% tab title="edition-validation.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onEmailValidator()">Email validator
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onHireDateValidator()">Hire Date validator
    </button>
</div>

<dike-grid id="grid-edition-validation" height="600px" #grid="dkgGrid">
</dike-grid>
```

{% endtab %}

{% tab title="edition-validation.component.ts" %}

```typescript
onEmailValidator(): void {
  const emailColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'email');

  // The column exists and is a data column:
  if (!!emailColumn && isDikeDataColumnDef(emailColumn)) {
    // Assign the email validator:
    this.dikeGrid.columnDef.setColumnEditable(emailColumn, true, {
      editionSettings: { ...this.emailEditionSettings, validators: [ Validators.email ] }
    });
  }
}

onHireDateValidator(): void {
  const hireDateColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'hireDate');

  // The column exists and is a data column:
  if (!!hireDateColumn && isDikeDataColumnDef(hireDateColumn)) {
    const today = new Date();
    // Assign the custom limitHireDateValidator validator:
    this.dikeGrid.columnDef.setColumnEditable(hireDateColumn, true, {
      editionSettings: {
        ...this.hireDateEditionSettings,
        validators: [ limitHireDateValidator(new Date(today.getFullYear(), today.getMonth(), today.getDate())) ],
        errorMessages: new CustomErrorMessage()
          .addMessage('limitHireDate', 'You can not enter a date after today.')
      }
    });
  }
}
```

{% endtab %}

{% tab title="custom-validator.ts" %}

```typescript
export const limitHireDateValidator = (limitDate: Date): ValidatorFn =>
    (control: AbstractControl): ValidationErrors | null => {
        if (!(control.value instanceof Date)) {
          return null;
        }

        // Get the Date from the input:
        const controlDate = control.value as Date;

        return controlDate.getTime() > limitDate.getTime() ? { limitHireDate: true } : null;
    };
```

{% endtab %}
{% endtabs %}

The previous definition generates the following output:

![Assigning validators using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FvYt97QlkS0WlUkY3PFtQ%2Fedition-validation-using-api.png?alt=media\&token=f9ce4542-ab3d-444b-ba50-e0f023d57932)

We describe the button actions in the following list:

1. **Email validator**. Clicking on this button will add the **email validator** to the **Email** column.
2. **Hire Date validator**. This action adds the **limitHireDateValidator** custom validator to the **Hire Date** column.

The **limitHireDateValidator** custom validator prevents users from entering dates after the current date.

![limitHireDate custom validator](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FioOa7UUJXnhUYAQA8RUZ%2Fedition-validation-using-api-limitHireDate.png?alt=media\&token=908af7fc-60b1-451a-92ca-b9b5ca2b13f4)

{% hint style="info" %}
When changing the validators at **runtime**, the DikeGrid re-renders the view for the involved columns.
{% endhint %}

## Summary

When using the Angular validator functions, the DikeGrid offers a set of validations messages. You can overwrite these validation messages or add new ones.&#x20;

Finally, you can create custom validation functions at the column or row levels. You can assign these validators at column definition or runtime.

{% tabs %}
{% tab title="edition-validation.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onEmailValidator()">Email validator
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onHireDateValidator()">Hire Date validator
    </button>
</div>

<dike-grid id="grid-edition-validation" height="600px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    allowEdition
    [editionMode]="gridProperties.editionMode"
    
    [gridEditionSettings]="gridEditionSettings"

    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350">
    </dike-grid-column>

    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            editable
            [editionSettings]="nameEditionSettings">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            editable
            [editionSettings]="lastNameEditionSettings">
        </dike-grid-column>
    </dike-grid-column>

    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="110"
        editable
        [editionSettings]="genderEditionSettings">
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="100"
        editable
        [editionSettings]="ageEditionSettings">
    </dike-grid-column>

    <dike-grid-column
        fieldName="email"
        headerText="Email"
        dataType="Text"
        width="300"
        editable
        [editionTemplate]="emailEdition"
        [editionSettings]="emailEditionSettings">
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        editable
        [editionSettings]="hireDateEditionSettings">
    </dike-grid-column>
</dike-grid>

<ng-template #emailEdition let-control="control">
    <mat-form-field floatLabel="never">
        <input matInput
            type="text"
            [formControl]="control"
            autocomplete="off">
        <mat-icon matSuffix>email</mat-icon>
    </mat-form-field>
</ng-template>
```

{% endtab %}

{% tab title="edition-validation.component.ts" %}

```typescript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { Validators } from '@angular/forms';
import { Subscription } from 'rxjs';

import { CustomErrorMessage, DikeGridComponent, DikeGridDataSourceInput, DikeGridEditionSettings,
    EditionFieldSettings, ErrorType, isDikeDataColumnDef
} from '@dikesoft/angular-data-grid';

import { forbiddenNameValidator, limitHireDateValidator } from './custom-validator';
import { menAgeCrossValidation } from './cross-validator';

import { DikeGridProperties } from 'app/core/config/dike-grid.properties';
import { Employee } from 'app/mock-api/common/employees/data.model';
import { SampleData } from 'app/services/sample-data.service';
import { DikeGridConfig } from 'app/services/dike-grid.config.service';

@Component({
  selector: 'edition-validation',
  templateUrl: './edition-validation.component.html',
  styleUrls: ['./edition-validation.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionValidationComponent implements OnInit, OnDestroy {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  nameEditionSettings: EditionFieldSettings;
  lastNameEditionSettings: EditionFieldSettings;
  genderEditionSettings: EditionFieldSettings;
  ageEditionSettings: EditionFieldSettings;
  emailEditionSettings: EditionFieldSettings;
  hireDateEditionSettings: EditionFieldSettings;

  gridEditionSettings: DikeGridEditionSettings<Employee>;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

  constructor(
    private cdr: ChangeDetectorRef,
    private gridConfig: DikeGridConfig,
    private sampleData: SampleData) { }

  ngOnInit(): void {
    // Get 1000 entries from the REST API:
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
    // Define the column edition settings:
    this.nameEditionSettings = {
      required: true,
      validators: [ Validators.maxLength(12), forbiddenNameValidator(/erick/i) ],
      errorMessages: new CustomErrorMessage()
        .addMessage(ErrorType.MAX_LENGTH, 'The Name column maximum length must be 12.')
        .addMessage('forbiddenName', 'The Name column can not contain the erick string.')
    };

    this.lastNameEditionSettings = {
      required: true,
      validators: [ Validators.maxLength(15) ],
      errorMessages: new CustomErrorMessage()
        .addMessage(ErrorType.MAX_LENGTH, 'The Surname column maximum length must be 15.')
    };

    this.genderEditionSettings = {
      required: true,
      options: [ { label: 'M', value: 'male' }, { label: 'F', value: 'female' } ]
    };

    this.ageEditionSettings = {
      required: true,
      validators: [ Validators.min(22), Validators.max(60) ],
      errorMessages: new CustomErrorMessage()
        .addMessage(ErrorType.REQUIRED, 'You must enter the age of the employee.')
        .addMessage(ErrorType.MIN, 'The minimum age to be hired must be 22 years old.')
        .addMessage(ErrorType.MAX, 'The maximum age to be hired must be 60 years old.')
    };

    this.emailEditionSettings = { required: true };
    this.hireDateEditionSettings = { required: true };

    this.gridEditionSettings = {
      crossFieldValidation: {
        customValidator: menAgeCrossValidation,
        errorMessages: new CustomErrorMessage()
          .addMessage('menAge', 'The minimum age for men is 36 years old.')
      }
    };
  }

  ngOnDestroy(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
  }

  onEmailValidator(): void {
    const emailColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'email');

    // The column exists and is a data column:
    if (!!emailColumn && isDikeDataColumnDef(emailColumn)) {
      // Assign the email validator:
      this.dikeGrid.columnDef.setColumnEditable(emailColumn, true, {
        editionSettings: { ...this.emailEditionSettings, validators: [ Validators.email ] }
      });
    }
  }

  onHireDateValidator(): void {
    const hireDateColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'hireDate');

    // The column exists and is a data column:
    if (!!hireDateColumn && isDikeDataColumnDef(hireDateColumn)) {
      const today = new Date();
      // Assign the custom limitHireDateValidator validator:
      this.dikeGrid.columnDef.setColumnEditable(hireDateColumn, true, {
        editionSettings: {
          ...this.hireDateEditionSettings,
          validators: [ limitHireDateValidator(new Date(today.getFullYear(), today.getMonth(), today.getDate())) ],
          errorMessages: new CustomErrorMessage()
            .addMessage('limitHireDate', 'You can not enter a date after today.')
        }
      });
    }
  }

  private setChangeGridPropertiesSubscription(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
    this.changeGridPropertiesSubscription = this.gridConfig.configChange.subscribe((props: DikeGridProperties) => {
      this.gridProperties = props;
      this.cdr.markForCheck();
    });
  }
}
```

{% endtab %}

{% tab title="custom-validator.ts" %}

```typescript
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

export const forbiddenNameValidator = (nameRe: RegExp): ValidatorFn =>
    (control: AbstractControl): ValidationErrors | null => nameRe.test(control.value) ? { forbiddenName: true } : null;

export const limitHireDateValidator = (limitDate: Date): ValidatorFn =>
    (control: AbstractControl): ValidationErrors | null => {
        if (!(control.value instanceof Date)) {
          return null;
        }

        // Get the Date from the input:
        const controlDate = control.value as Date;

        return controlDate.getTime() > limitDate.getTime() ? { limitHireDate: true } : null;
    };

```

{% endtab %}

{% tab title="cross-validator.ts" %}

```typescript
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
import { DikeGridColumnDef } from '@dikesoft/angular-data-grid';

import { Employee } from 'app/mock-api/common/employees/data.model';

// This function validates that men must be older than 35 years old:
export const menAgeCrossValidation = (columnDef: DikeGridColumnDef<Employee>): ValidatorFn => {

    // Get the desired columns:
    const genderColumn = columnDef.findColumn(column => column.fieldName === 'gender');
    const ageColumn = columnDef.findColumn(column => column.fieldName === 'age');

    // This validator function is attached to the row's FormGroup:
    return (rowGroup: AbstractControl): ValidationErrors | null => {
        // If the columns can not be found, there is no error:
        if (!genderColumn || !ageColumn) {
            return null;
        }

        // Get the controls from the corresponding FormGroup.
        // The slotId property identifies each column control inside the corresponding FormGroup:
        const genderControl = rowGroup.get(genderColumn.panel)?.get(genderColumn.slotId);
        const ageControl = rowGroup.get(ageColumn.panel)?.get(ageColumn.slotId);

        // If the controls can not be found, there is no error:
        if (!genderControl || !ageControl) {
            return null;
        }

        return genderControl.value as string === 'male' && ageControl.value as number <= 35 ? { menAge: true } : null;
    };
};
```

{% endtab %}

{% tab title="editing.module.ts" %}

```typescript
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

import { CustomErrorMessage, CUSTOM_EDITION_ERROR_MESSAGES, DikeDataGridModule,
    ErrorType, MAX_ROWS_IN_EDITION, ROW_EDITION_ESC_KEY,
    WAIT_FOR_MULTIPLE_ROWS_CANCELATION
} from '@dikesoft/angular-data-grid';

import { SharedModule } from 'app/shared/shared.module';
import { editingRoutes } from 'app/modules/admin/editing/editing.routing';

import { RowEditionComponent } from './row-edition/row-edition.component';
import { EditionTemplatesComponent } from './edition-templates/edition-templates.component';
import { EditionValidationComponent } from './edition-validation/edition-validation.component';
import { MultipleRowsEditionComponent } from './multiple-rows-edition/multiple-rows-edition.component';

@NgModule({
  declarations: [
    RowEditionComponent,
    EditionTemplatesComponent,
    EditionValidationComponent,
    MultipleRowsEditionComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild(editingRoutes),

    SharedModule,
    DikeDataGridModule
  ],
  providers: [
    { provide: ROW_EDITION_ESC_KEY, useValue: false },
    { provide: CUSTOM_EDITION_ERROR_MESSAGES,
      useFactory: (): CustomErrorMessage =>
        new CustomErrorMessage()
            .addMessage(ErrorType.REQUIRED, 'You can not leave the field empty.')
    },
    { provide: MAX_ROWS_IN_EDITION, useValue: 5 },
    { provide: WAIT_FOR_MULTIPLE_ROWS_CANCELATION, useValue: false }
  ]
})
export class EditingModule { }

```

{% endtab %}
{% endtabs %}
