# Edition templates

## Live example

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

## Default templates

It is enough to make a column **editable** to modify the column value. The DikeGrid instance will display the default templates depending on the column type. See the following table.

| Column type | Default template                                                                                       |
| ----------- | ------------------------------------------------------------------------------------------------------ |
| `Text`      | A Material Form field with an input element with type equals <mark style="color:red;">`text`</mark>.   |
| `Numeric`   | A Material Form field with an input element with type equals <mark style="color:red;">`number`</mark>. |
| `Date`      | A Material Form field with a Material Datepicker inside.                                               |
| `Binary`    | A Material checkbox.                                                                                   |

Let us make some columns editable:

{% code title="edition-templates.component.html" %}

```markup
<dike-grid id="grid-edition-templates" height="600px" #grid="dkgGrid"
    allowEdition
    [editionMode]="gridProperties.editionMode">

    <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>
        </dike-grid-column>

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

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

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

    <dike-grid-column
        fieldName="email"
        headerText="Email"
        dataType="Text"
        width="300"
        editable>
    </dike-grid-column>

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

{% endcode %}

The previous initial configuration generates the following output:

![Default Edition templates](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FQjhWzT9ju8Y2XHPKk8wb%2Fedition-templates-default.png?alt=media\&token=9b18ed5d-c489-442f-8699-9088f8491012)

In general, the default templates achieve the edition operations well. However, the problem arises with the `Binary` columns when these do not hold boolean values.

As you can see, our **Gender** column values are <mark style="color:red;">`female`</mark> and <mark style="color:red;">`male`</mark>. Therefore, those values do not match in a checkbox.

Before providing our custom templates, let us explore a slight variation in default templates.

### Multiple Selection

Sometimes, you want the user to choose a value from a fixed number of options.

To achieve the previous task, you can provide an array of options to the default template. The DikeGrid instance will create a template with a **Material Select-control** inside a Form field.

You must provide a set of options depending on the column type:

| Column type | Array type                                                    |
| ----------- | ------------------------------------------------------------- |
| `Text`      | <mark style="color:green;">`DikeTextSelectionModel`</mark>    |
| `Numeric`   | <mark style="color:green;">`DikeNumericSelectionModel`</mark> |
| `Binary`    | <mark style="color:green;">`DikeBinarySelectionModel`</mark>  |

Let us provide options to our **Gender** and **Age** default templates.

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

```markup
<dike-grid-column
    fieldName="gender"
    headerText="Gender"
    dataType="Binary"
    width="110"
    editable
    [editionSettings]="{ required: true, options: [ { label: 'M', value: 'male' }, { label: 'F', value: 'female' } ] }">
</dike-grid-column>

<dike-grid-column
    fieldName="age"
    headerText="Age"
    dataType="Numeric"
    contentAlign="center"
    width="100"
    editable
    [editionSettings]="{ options: ageOptions }">
</dike-grid-column>
```

{% endtab %}

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

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionTemplatesComponent implements OnInit, OnDestroy {
  ageOptions: DikeNumericSelectionModel[];
  
  ngOnInit(): void {
    //...
    
    // Define mutiple options for the Age column:
    this.ageOptions = [
        { label: '27', value: 27 },
        { label: '36', value: 36 },
        { label: '40', value: 40 },
        { label: '42', value: 42 },
        { label: '43', value: 43 },
        { label: '50', value: 50 },
        { label: '52', value: 52 },
        { label: '53', value: 53 }
      ];
  }
}
```

{% endtab %}
{% endtabs %}

![Default edition templates - Multiple options](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FWnFFMvYQtieMEunfGaBZ%2Fedition-templates-multiple-options.png?alt=media\&token=0a349c37-e065-47e5-8811-509301c6640d)

{% hint style="warning" %}
For `Binary` columns, the DikeGrid will take the first two options of the provided set. The DikeGrid will ignore the other options if you offer more than two options.
{% endhint %}

{% hint style="info" %}
For `Text` and `Numeric` columns, you can provide any number of options.
{% endhint %}

## Custom templates

You can provide your custom templates if you need a more specific template for editing your column values.

See the following custom templates:

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

```markup
<dike-grid id="grid-edition-templates" height="600px" #grid="dkgGrid"
    (gridColumnDefInstance)="onColumnDefInstance($event)">
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="110"
        editable
        [editionSettings]="{ required: true, options: [ { label: 'M', value: 'male' }, { label: 'F', value: 'female' } ] }"
        [editionTemplate]="genderEdition">
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="email"
        headerText="Email"
        dataType="Text"
        width="300"
        editable
        [editionTemplate]="emailEdition">
    </dike-grid-column>
</dike-grid>

<ng-template #genderEdition let-control="control" let-options="options">
    <mat-radio-group class="flex flex-row justify-between" [formControl]="control">
      <mat-radio-button *ngFor="let option of options" [value]="option.value">{{ option.label }}</mat-radio-button>
    </mat-radio-group>
</ng-template>

<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>

<ng-template #totalSales let-value="fieldValue">
    <p>{{ value | currency }}</p>
</ng-template>

<ng-template #totalSalesEdition let-control="control">
    <mat-form-field floatLabel="never">
        <input matInput
            type="number"
            [formControl]="control"
            autocomplete="off">
        <mat-icon matPrefix>attach_money</mat-icon>
    </mat-form-field>
</ng-template>
```

{% endtab %}

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

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionTemplatesComponent implements OnInit, OnDestroy {
  // Retrieve the Total sales templates:
  @ViewChild('totalSales', { read: TemplateRef }) totalSalesTpl: TemplateRef<any>;
  @ViewChild('totalSalesEdition', { read: TemplateRef }) totalSalesEditionTpl: TemplateRef<any>;
  
  //...
  
  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Total Sales column:
    const totalSalesColumn = new DikeNumericColumnDef<Employee>('totalSales', 'Total Sales');
    totalSalesColumn.width = 230;
    totalSalesColumn.order = 7;
    totalSalesColumn.editable = true;
    totalSalesColumn.editionSettings = { required: true };
    // Since templates will be available after the view is initialized, we schedule the assignment at the end of the event loop:
    totalSalesColumn.displayTemplate = from(Promise.resolve().then(() => this.totalSalesTpl));
    totalSalesColumn.editionTemplate = from(Promise.resolve().then(() => this.totalSalesEditionTpl));

    columnDef.addColumns(totalSalesColumn);
  }
}
```

{% endtab %}
{% endtabs %}

1. We have defined templates for the **Gender**, **Email**, and **Total Sales** columns.
2. We take the options we set to the default template to provide them to our custom template for the **Gender** column.
3. We intentionally defined the **Total Sales** column in the code. See how we assign the template.

![Custom Edition templates](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fmsm2sFBnZGQJtK8G5XGr%2Fedition-templates-custom.png?alt=media\&token=b9933fe5-1747-4207-8dd6-be38d8120c3b)

{% hint style="success" %}
You will have access to the corresponding <mark style="color:green;">`FormControl`</mark> and the options provided in the [<mark style="color:green;">`EditionFieldSettings`</mark>](https://docs.dikesoft.com/reference/interfaces/editing#editionfieldsettings) object.
{% endhint %}

{% hint style="info" %}
The <mark style="color:orange;">`editionTemplate`</mark> and <mark style="color:orange;">`displayTemplate`</mark> properties accept a <mark style="color:green;">`TemplateRef`</mark> or an <mark style="color:green;">`Observable`</mark> of a <mark style="color:green;">`TemplateRef`</mark>.
{% endhint %}

## Assigning a template using the API

You can assign a custom template to a column or set the default template again.

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

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

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

{% endtab %}

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

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

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

{% endtab %}
{% endtabs %}

### DikeGrid Column API

You can set edition templates at **runtime** using the **column API**.

The methods regarding edition templates are:

| Method                              | Description                                                                |
| ----------------------------------- | -------------------------------------------------------------------------- |
| `setColumnEditable()`               | Use this method to set the edition template to the given column.           |
| `setDefaultEditionTemplateColumn()` | You can establish the default edition template for the given column again. |

{% 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 %}

To see the API in action, consider the following configuration:

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

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDefaultTemplate('gender')">Gender default template
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onCustomTemplate('gender')">Gender custom template
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDefaultTemplate('email')">Email default template
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onCustomTemplate('email')">Email custom template
        </button>
    </div>
</div>

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

{% endtab %}

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

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionTemplatesComponent implements OnInit, OnDestroy {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;
  // Retrieve the Total sales templates:
  @ViewChild('genderEdition', { read: TemplateRef }) genderEditionTpl: TemplateRef<any>;
  @ViewChild('emailEdition', { read: TemplateRef }) emailEditionTpl: TemplateRef<any>;
  
  //...
  
  onCustomTemplate(field: string): void {
    const givenColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === field);

    // The column exists and is a data column:
    if (!!givenColumn && isDikeDataColumnDef(givenColumn)) {
      // Get the custom edition template:
      const columnTemplate = field === 'gender' ? this.genderEditionTpl : this.emailEditionTpl;
      // Assign the custom template for the given column:
      this.dikeGrid.columnDef.setColumnEditable(givenColumn, true, { editionTemplate: columnTemplate });
    }
  }

  onDefaultTemplate(field: string): void {
    const givenColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === field);

    // The column exists and is a data column:
    if (!!givenColumn && isDikeDataColumnDef(givenColumn)) {
      this.dikeGrid.columnDef.setDefaultEditionTemplateColumn(givenColumn);
    }
  }
}
```

{% endtab %}
{% endtabs %}

The previous definition generates the following output:

![Assigning edition templates - Using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FSzBAFLkV4B4pzBI7M6Jn%2Fedition-templates-toggle-templates.png?alt=media\&token=c4cb6fb2-d5fc-4b9b-a7d2-aca018d6186e)

We have added four buttons:

1. **Gender default template**. When you click on this button, you will set the default template again.
2. **Gender custom template**. This action will assign the custom edition template.
3. **Email default template**. It will establish the default edition template.
4. **Email custom template**. You will set the custom edition template when you click on this button.

{% hint style="info" %}
As you can see, when changing the template, the DikeGrid re-renders the view for that column.
{% endhint %}

## Summary

The DikeGrid definition offers you a default template for each column type. Nonetheless, you can provide custom edition templates when defining a column or using the API.

### Complete code for this section.

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

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDefaultTemplate('gender')">Gender default template
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onCustomTemplate('gender')">Gender custom template
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDefaultTemplate('email')">Email default template
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onCustomTemplate('email')">Email custom template
        </button>
    </div>
</div>

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

    allowEdition
    [editionMode]="gridProperties.editionMode"

    (gridColumnDefInstance)="onColumnDefInstance($event)"

    [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>
        </dike-grid-column>

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

    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="110"
        editable
        [editionSettings]="{ required: true, options: [ { label: 'M', value: 'male' }, { label: 'F', value: 'female' } ] }"
        [editionTemplate]="genderEdition">
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="100"
        editable
        [editionSettings]="{ options: ageOptions }">
    </dike-grid-column>

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

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

<ng-template #genderEdition let-control="control" let-options="options">
    <mat-radio-group class="flex flex-row justify-between" [formControl]="control">
      <mat-radio-button *ngFor="let option of options" [value]="option.value">{{ option.label }}</mat-radio-button>
    </mat-radio-group>
</ng-template>

<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>

<ng-template #totalSales let-value="fieldValue">
    <p>{{ value | currency }}</p>
</ng-template>

<ng-template #totalSalesEdition let-control="control">
    <mat-form-field floatLabel="never">
        <input matInput
            type="number"
            [formControl]="control"
            autocomplete="off">
        <mat-icon matPrefix>attach_money</mat-icon>
    </mat-form-field>
</ng-template>

```

{% endtab %}

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

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

import { DikeGridColumnDef, DikeGridComponent, DikeGridDataSourceInput,
    DikeNumericColumnDef, DikeNumericSelectionModel, isDikeDataColumnDef
} from '@dikesoft/angular-data-grid';

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-templates',
  templateUrl: './edition-templates.component.html',
  styleUrls: ['./edition-templates.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditionTemplatesComponent implements OnInit, OnDestroy {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;
  // Retrieve the Total sales templates:
  @ViewChild('totalSales', { read: TemplateRef }) totalSalesTpl: TemplateRef<any>;
  @ViewChild('totalSalesEdition', { read: TemplateRef }) totalSalesEditionTpl: TemplateRef<any>;
  @ViewChild('genderEdition', { read: TemplateRef }) genderEditionTpl: TemplateRef<any>;
  @ViewChild('emailEdition', { read: TemplateRef }) emailEditionTpl: TemplateRef<any>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  ageOptions: DikeNumericSelectionModel[];

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Total Sales column:
    const totalSalesColumn = new DikeNumericColumnDef<Employee>('totalSales', 'Total Sales');
    totalSalesColumn.width = 230;
    totalSalesColumn.order = 7;
    totalSalesColumn.editable = true;
    totalSalesColumn.editionSettings = { required: true };
    // Since templates will be available after the view is initialized, we schedule the assignment at the end of the event loop:
    totalSalesColumn.displayTemplate = from(Promise.resolve().then(() => this.totalSalesTpl));
    totalSalesColumn.editionTemplate = from(Promise.resolve().then(() => this.totalSalesEditionTpl));

    columnDef.addColumns(totalSalesColumn);
  }

  ngOnInit(): void {
    // Get 1000 entries from the REST API:
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
    // Define mutiple options for the Age column:
    this.ageOptions = [
        { label: '27', value: 27 },
        { label: '36', value: 36 },
        { label: '40', value: 40 },
        { label: '42', value: 42 },
        { label: '43', value: 43 },
        { label: '50', value: 50 },
        { label: '52', value: 52 },
        { label: '53', value: 53 }
      ];
  }

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

  onCustomTemplate(field: string): void {
    const givenColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === field);

    // The column exists and is a data column:
    if (!!givenColumn && isDikeDataColumnDef(givenColumn)) {
      // Get the custom edition template:
      const columnTemplate = field === 'gender' ? this.genderEditionTpl : this.emailEditionTpl;
      // Assign the custom template for the given column:
      this.dikeGrid.columnDef.setColumnEditable(givenColumn, true, { editionTemplate: columnTemplate });
    }
  }

  onDefaultTemplate(field: string): void {
    const givenColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === field);

    // The column exists and is a data column:
    if (!!givenColumn && isDikeDataColumnDef(givenColumn)) {
      this.dikeGrid.columnDef.setDefaultEditionTemplateColumn(givenColumn);
    }
  }

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

{% endtab %}
{% endtabs %}
