# Overview

To help you understand every section, we have created a live demo for each DkGrid feature.

## DkGrid Examples

We have deployed all the DkGrid features as **live** **examples**. We describe every DkGrid part showing a code snippet from its corresponding live demo.

A live-demo link looks as follows:

{% hint style="success" %}
[Getting Started](https://demos.dikesoft.com/dk-grid/getting-started) live example.
{% endhint %}

### Live Examples Themes and Schemes

When navigating the live examples, you can change the **Theme** and **Scheme** at run time. The following themes are available in `light` and `dark` schemes:

| Theme name   | Palettes                  |
| ------------ | ------------------------- |
| default      | blue, amber, red          |
| brand        | dikesoft-blue, amber, red |
| indigo       | indigo, pink, red         |
| deep-purple  | deep-purple, amber, red   |
| pink         | pink, blue-gray, red      |
| purple       | purple, amber, red        |
| custom-green | green, yellow, red        |

{% hint style="info" %}
Except for dikesoft-blue, we have taken all palettes from the [Material Design Color System](https://material.io/design/color/the-color-system.html).
{% endhint %}

### Styles generation

We have used Tailwind CSS to generate all the above themes and schemes. We also use the predefined classes offered by Tailwind CSS.

{% hint style="info" %}
Visit the [Tailwind CSS](https://tailwindcss.com/docs/installation) official docs for further details.
{% endhint %}

### Changing some DkGrid properties at runtime

As you can change the Theme and the Scheme, you can also change some DkGrid properties when interacting with the live examples.

The following are all the properties you can change. These properties feed the corresponding DkGrid properties.

{% hint style="info" %}
We bind the DkGrid properties corresponding to the DkGrid feature we are describing.
{% endhint %}

#### Size

| Property    | Description          | Default value | Validators |
| ----------- | -------------------- | ------------- | ---------- |
| Grid Width  | The DkGrid's width.  | auto          | Required   |
| Grid Height | The DkGrid's height. | 500px         | Required   |

#### Appearance

<table><thead><tr><th>Property</th><th width="361.3333333333333">Description</th><th width="155">Default value</th><th>Validators</th></tr></thead><tbody><tr><td>Material Elevation</td><td>Indicates if the DkGrid will express elevation.</td><td>true</td><td></td></tr><tr><td>Elevation value</td><td>The elevation value the DkGrid will take.</td><td>2</td><td>Required<br>Min value: 0<br>Max value: 24<br>Only numbers</td></tr><tr><td>Vertical Row Lines</td><td>Indicates if the lines that separate every column value will be shown.</td><td>false</td><td></td></tr><tr><td>Striped</td><td>Indicates if one row will be distinguished from the other.</td><td>true</td><td></td></tr></tbody></table>

#### Header

<table><thead><tr><th>Property</th><th width="249">Description</th><th width="150">Default value</th><th>Validators</th></tr></thead><tbody><tr><td>Column Context Menu</td><td>It allows showing or hiding the context menu for all the DkGrid columns.</td><td>true</td><td></td></tr><tr><td>Header Height</td><td>Sets the height, in pixels, for every column header.</td><td>60</td><td>Required<br>Min value: 40<br>Max value: 120<br>Only numbers</td></tr></tbody></table>

#### Dragging

<table><thead><tr><th width="197.86078755719777">Property</th><th width="372.69980188915196">Description</th><th>Default value</th></tr></thead><tbody><tr><td>Allow column dragging</td><td>A flag that indicates if the DkGrid allows its columns to be dragged.</td><td>false</td></tr></tbody></table>

#### Rows

<table><thead><tr><th>Property</th><th width="376">Description</th><th width="150">Default value</th><th>Validators</th></tr></thead><tbody><tr><td>Row Id</td><td>A flag that allows displaying the id of every row.</td><td>false</td><td></td></tr><tr><td>Column Id width</td><td>Establishes the width, in pixels, of the column that displays the Row Id.</td><td>70</td><td>Required<br>Min value: 50<br>Max value: 100<br>Only numbers</td></tr><tr><td>Content Row Height</td><td>Establishes the height, in pixels, of every displayed row.</td><td>50</td><td>Required<br>Min value: 45<br>Max value: 80<br>Only numbers</td></tr></tbody></table>

#### Row Grouping

<table><thead><tr><th>Property</th><th width="383">Description</th><th width="150">Default value</th><th>Validators</th></tr></thead><tbody><tr><td>Row Grouping Height</td><td>The height, in pixels, of the panel where the columns are displayed, which the DkGrid is grouped by.</td><td>71</td><td>Required<br>Min value: 50<br>Max value: 100<br>Only numbers</td></tr><tr><td>Columns Height</td><td>The height, in pixels, of every column that is displayed in the Row-Grouping panel.</td><td>36</td><td>Required<br>Min value: 30<br>Max value: 80<br>Only numbers</td></tr><tr><td>Row Grouping Indent</td><td>A flag that allows displaying or hiding the indentation of every row under a group.</td><td>true</td><td></td></tr><tr><td>Indent width</td><td>The width, in pixels, of the indentation every row under a group will have.</td><td>25</td><td>Required<br>Min value: 25<br>Max value: 150<br>Only numbers</td></tr></tbody></table>

#### Sorting

<table><thead><tr><th width="200.33333333333331">Property</th><th width="392.258956364289">Description</th><th>Default value</th></tr></thead><tbody><tr><td>Allow sorting</td><td>Indicates if the DkGrid will allow or not that its rows could be ordered.</td><td>false</td></tr></tbody></table>

#### Filtering

<table><thead><tr><th>Property</th><th width="200">Description</th><th>Default value</th><th width="173">Validators</th></tr></thead><tbody><tr><td>Row Filter</td><td>The flag that allows if the row, under the headers, will be shown.</td><td>false</td><td></td></tr><tr><td>Row Filter Height</td><td>The height, in pixels, that the row filter will have.</td><td>55</td><td>Required<br>Min value: 45<br>Max value: 80<br>Only numbers</td></tr><tr><td>On-Demand filter</td><td>This flag allows filtering rows until the user sends the conditions by clicking the corresponding button.</td><td>false</td><td></td></tr></tbody></table>

#### Selection

<table><thead><tr><th>Property</th><th width="403.3333333333333">Description</th><th>Default value</th></tr></thead><tbody><tr><td>Allow selection</td><td>The flag that allows that every row could be selected.</td><td>false</td></tr></tbody></table>

#### Edition

<table><thead><tr><th>Property</th><th width="413">Description</th><th width="150">Default value</th><th>Validators</th></tr></thead><tbody><tr><td>Edition mode</td><td>This flag is used to enable or disable the rows edition, if the DkGrid allows edition as well.</td><td>false</td><td></td></tr><tr><td>Edition Toolbar</td><td>This flag allows displaying or hiding the edition toolbar.</td><td>false</td><td></td></tr><tr><td>Toolbar Height</td><td>The height, in pixels, that the Edition Toolbar will have.</td><td>55</td><td>Required<br>Min value: 45<br>Max value: 100<br>Only numbers</td></tr><tr><td>Toolbar Item Height</td><td>The height, in pixels, of every option of the Edition Toolbar.</td><td>42</td><td>Required<br>Min value: 36<br>Max value: 60<br>Only numbers</td></tr><tr><td>Toolbar position</td><td>The value indicates if the Edition Toolbar is shown at the top or bottom of the DkGrid.</td><td><mark style="color:orange;"><code>bottom</code></mark></td><td></td></tr><tr><td>Toolbar alignment</td><td>The value indicates if the Edition Toolbar is shown at the left, center or right.</td><td><mark style="color:orange;"><code>left</code></mark></td><td></td></tr></tbody></table>

#### Pagination

<table><thead><tr><th>Property</th><th width="419">Description</th><th width="150">Default value</th><th>Validators</th></tr></thead><tbody><tr><td>Pagination</td><td>This flag allows paginating the data set.</td><td>false</td><td></td></tr><tr><td>Page size</td><td>The value indicates how many rows will be displayed for every page.</td><td>50</td><td>Required<br>Min value: 1<br>Only numbers</td></tr><tr><td>Pagination Height</td><td>The height, in pixel, that the paginator will have.</td><td>64</td><td>Required<br>Min value: 60<br>Max value: 100<br>Only numbers</td></tr></tbody></table>

### Floating Configuration Panel

While navigating the live examples, you will see a red square with white gear.

![Configuration white gear](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fu12Ck2AmCANceBg4JiOS%2Foverview-white-gear.png?alt=media\&token=800e7cf1-652b-4d8d-8f5d-21bc01965eda)

Clicking on the white gear will show a floating panel with all the themes, schemes, and DkGrid properties.

![Themes, Schemes and DkGrid properties](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F2Yd2rHN3V60djyafDvPX%2Ffloating-panel-conf.png?alt=media\&token=ee6f6d2f-57ad-4e1a-b2d3-c2647f050849)

{% hint style="info" %}
You can click on the circular arrow icon to reset the values for each group of properties.
{% endhint %}

### Setting properties

The DkGrid changes all the described properties through a service called <mark style="color:green;">`DkGridConfig`</mark>. This service is responsible for delivering every property when changing one of them through the [Floating Configuration Panel](#floating-configuration-panel).

{% code title="dk-grid.config.service.ts" lineNumbers="true" %}

```typescript
import { Injectable } from '@angular/core';

import { BehaviorSubject, Observable } from 'rxjs';

import { DkGridProperties, gridSize, gridAppearance, gridHeader,
  gridRows, gridRowGrouping, gridSorting, gridFitlering,
  gridSelection, gridEdition, gridDragging, gridPagination
} from 'app/core/config/dk-grid.properties';

@Injectable({
  providedIn: 'root'
})
export class DkGridConfig {

  private gridConfigSubject: BehaviorSubject<DkGridProperties>;

  constructor() {
    this.gridConfigSubject = new BehaviorSubject<DkGridProperties>(this.getDefaultProperties());
  }

  get configChange(): Observable<DkGridProperties> {
    return this.gridConfigSubject.asObservable();
  }

  get properties(): DkGridProperties {
    return this.gridConfigSubject.value;
  }

  getDefaultProperties(): DkGridProperties {
    return {
      ...gridSize, ...gridAppearance, ...gridHeader, ...gridRows,
      ...gridRowGrouping, ...gridSorting, ...gridFitlering, ...gridSelection,
      ...gridEdition, ...gridDragging, ...gridPagination
    };
  }

  changeGridConfig(value: DkGridProperties): void {
    this.gridConfigSubject.next(value);
  }
}

```

{% endcode %}

{% hint style="warning" %}
Remember that the <mark style="color:green;">`DkGridConfig`</mark> service does not belong to the <mark style="color:green;">`DkGridComponent`</mark> definition itself. It was created only for demo purposes.
{% endhint %}

As you can see in the <mark style="color:green;">`DkGridConfig`</mark> definition, it has a public property called `configChange` of type <mark style="color:green;">`Observable<DkGridProperties>`</mark>. Therefore, If a live-demo feature should react to these properties changes, the corresponding <mark style="color:green;">`DkGridComponent`</mark> instance must listen to the `configChange` observable.

We have grouped all the properties in one type called <mark style="color:green;">`DkGridProperties`</mark>, an Intersection type.

{% code title="dk-grid.properties.ts" lineNumbers="true" %}

```typescript
import { EditionToolbarPosition, EditionToolbarAlignment } from '@dk-grid-standard/angular';

export interface DkGridSize {
    width: string;
    height: string;
}
export interface DkGridAppearance {
    matElevation: boolean;
    elevationValue: number;
    verticalRowLines: boolean;
    stripeRows: boolean;
}

export interface DkGridHeader {
    columnContextMenu: boolean;
    headerHeight: number;
}

export interface DkGridDragging {
    allowColumnDragging: boolean;
}

export interface DkGridRows {
    displayRowId: boolean;
    rowIdWidth: number;
    contentRowHeight: number;
}

export interface DkGridRowGrouping {
    rowGroupingHeight: number;
    rowGroupingColumnHeight: number;
    displayRowGroupingIndent: boolean;
    rowGroupingIndentWidth: number;
}

export interface DkGridSorting {
    allowSorting: boolean;
}

export interface DkGridFiltering {
    allowRowFilter: boolean;
    filterRowHeight: number;
    onDemandFilter: boolean;
}

export interface DkGridSelection {
    allowSelection: boolean;
}

export interface DkGridEdition {
    editionMode: boolean;
    displayEditionToolbar: boolean;
    editionToolbarHeight: number;
    edtitionToolbarItemHeight: number;
    editionToolbarPosition: EditionToolbarPosition;
    editionToolbarAlignment: EditionToolbarAlignment;
}

export interface DkGridPagination {
    allowPagination: boolean;
    pageSize: number;
    paginationHeight: number;
}

export type DkGridProperties = DkGridSize & DkGridAppearance & DkGridHeader &
    DkGridRows & DkGridRowGrouping & DkGridSorting &
    DkGridFiltering & DkGridSelection & DkGridEdition &
    DkGridDragging & DkGridPagination;

export const gridSize: DkGridSize = {
    width: 'auto',
    height: '500px'
};

export const gridAppearance: DkGridAppearance = {
    matElevation: true,
    elevationValue: 2,
    stripeRows: true,
    verticalRowLines: false
};

export const gridHeader: DkGridHeader = {
    columnContextMenu: true,
    headerHeight: 60
};

export const gridDragging: DkGridDragging = {
    allowColumnDragging: false
};

export const gridRows: DkGridRows = {
    displayRowId: false,
    rowIdWidth: 70,
    contentRowHeight: 50
};

export const gridRowGrouping: DkGridRowGrouping = {
    rowGroupingHeight: 71,
    displayRowGroupingIndent: true,
    rowGroupingIndentWidth: 25,
    rowGroupingColumnHeight: 36
};

export const gridSorting: DkGridSorting = {
    allowSorting: false
};

export const gridFitlering: DkGridFiltering = {
  allowRowFilter: false,
  onDemandFilter: false,
  filterRowHeight: 55
};

export const gridSelection: DkGridSelection = {
  allowSelection: false
};

export const gridEdition: DkGridEdition = {
  editionMode: false,
  displayEditionToolbar: false,
  editionToolbarHeight: 55,
  edtitionToolbarItemHeight: 42,
  editionToolbarAlignment: 'left',
  editionToolbarPosition: 'bottom'
};

export const gridPagination: DkGridPagination = {
  allowPagination: false,
  pageSize: 50,
  paginationHeight: 64
};

```

{% endcode %}

{% hint style="warning" %}
We have created these interfaces for demo purposes. Therefore, every property of these interfaces feeds the corresponding <mark style="color:green;">`DkGridComponent`</mark> properties.
{% endhint %}

## Sample Data

We generate sample data of **1000 rows** or even more. The service <mark style="color:green;">`SampleData`</mark> is responsible for this data generation.

{% hint style="info" %}
We have generated only five rows for the [Quick Start Tutorial](/getting-started/quick-start-tutorial#binding-the-datasource) by hand.
{% endhint %}

### Shape of data

The main interface is <mark style="color:green;">`Employee`</mark>. This interface contains all the fields that we display in the live examples.

{% code title="data.model.ts" lineNumbers="true" %}

```typescript
export interface Employee {
  employeeId: string;
  firstName: string;
  lastName: string;
  gender: string;
  age: number;

  country: string;
  hireDate: Date;

  email: string;

  sales: number[];
  totalSales: number;
  bonus: number;
  performance: number;
}

export const months: string[] = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December'
];

```

{% endcode %}

## Conventions

For a better experience reading this set of docs, we have taken some conventions:

1. Any type, even primitives, are highlighted in the text in green color. For example, <mark style="color:green;">`Date`</mark>, <mark style="color:green;">`string`</mark>, <mark style="color:green;">`numeric`</mark>, <mark style="color:green;">`DkGridComponent`</mark>, <mark style="color:green;">`Employee`</mark>.
2. HTML selectors are highlighted in the text in red color. For example, <mark style="color:red;">`div`</mark>, <mark style="color:red;">`section`</mark>, <mark style="color:red;">`dk-grid`</mark>, <mark style="color:red;">`dk-grid-column`</mark>.
3. Properties for the <mark style="color:red;">`dk-grid`</mark> and <mark style="color:red;">`dk-grid-column`</mark> selectors are highlighted in text in orange color. For example, <mark style="color:orange;">`allowSorting`</mark>, <mark style="color:orange;">`sortable`</mark>, <mark style="color:orange;">`allowRowGrouping`</mark>, <mark style="color:orange;">`draggable`</mark>, <mark style="color:orange;">`filterable`</mark>.
4. Injection Tokens are highlighted in blue color. For example, <mark style="color:blue;">`HEADER_ROW_HEIGHT`</mark>, <mark style="color:blue;">`LOADING_MESSAGE`</mark>.


# Quick Start Tutorial

This section summarizes all the essentials you need to know to start with DkGrid. Here you can find how to create a DkGrid instance, add some columns, and enable some features.

## Live example

After completing this tutorial, you will get something similar to the live example.

{% hint style="success" %}
[Getting Started](https://demos.dikesoft.com/dk-grid/getting-started) live example.
{% endhint %}

## Creating an Angular app

Before using the **DkGrid Library,** you must create an Angular app. Then, follow the steps described in the Angular official docs to [set up your local environment](https://angular.io/guide/setup-local) for Angular development.

{% hint style="warning" %}
The **DkGrid Library** has the following Angular packages as peer dependencies:

* @angular/common
* @angular/core
* @angular/forms
* @angular/cdk
* @angular/material
* rxjs
  {% endhint %}

{% hint style="success" %}
At the time of this writing, we support **Angular v12**, **v13**, and **v14**.
{% endhint %}

## Adding Angular Material

Since the **DkGrid Library** uses some **CDK** packages and many **Angular Material** components, you must install the Angular Material package.

Following the [Angular Material official docs](https://material.angular.io/guide/getting-started), in the root of your Angular app, open a terminal and run the following command:

```
ng add @angular/material
```

The `ng-add` command will install **Angular Material**, the **Component Dev Kit (CDK)**, and **Angular Animations**. During the installation process, some questions will be prompted.&#x20;

{% hint style="info" %}
For this tutorial, you can select a [pre-built material design theme](https://material.angular.io/guide/theming#using-a-pre-built-theme).
{% endhint %}

## Adding DkGrid to your project

### Installing the NPM package

We distribute the **DkGrid Library** as **NPM** package. Once you have set up your Angular environment, you can add the **DkGrid Library** to your project.

At the root of your project, open a terminal and run the following command:

```
npm i @dk-grid-standard/angular
```

{% hint style="success" %}
If you are using the **Community Edition**, run the following command:&#x20;

npm i @dk-grid-community/angular
{% endhint %}

{% hint style="info" %}
Be aware of pulling the correct version according to your Angular version.
{% endhint %}

### Adding the DkGrid Angular module

After installing the corresponding DkGrid NPM package in your development environment, **add** the **DkGrid module** to the app module.

{% code title="app.module.ts" lineNumbers="true" %}

```typescript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatNativeDateModule } from '@angular/material/core';
import { DkDataGridModule } from '@dk-grid-standard/angular';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MatNativeDateModule,
    
    DkDataGridModule
  ],
  providers: [ ],
  bootstrap: [AppComponent],
})
export class AppModule { }

```

{% endcode %}

{% hint style="warning" %}
If your data set has date fields, do not forget to import the <mark style="color:green;">MatNativeDateModule</mark> or a custom implementation instead.

Internally, the DkGrid uses the native <mark style="color:green;">Date</mark> type. Therefore, your custom implementation must also manage date fields using the native <mark style="color:green;">Date</mark> type.
{% endhint %}

## Adding the DkGrid Styles

The DkGrid package comes with four pre-built theme CSS files. Each of them uses the same palettes as Angular Material pre-built themes. Indeed the file names are the same.

<table><thead><tr><th width="338.11648644704013">Theme</th><th width="150">Scheme</th><th>Palettes</th></tr></thead><tbody><tr><td><code>deeppurple-amber.css</code></td><td>Light</td><td>deep-purple, amber, red</td></tr><tr><td><code>indigo-pink.css</code></td><td>Light</td><td>indigo, pink, red</td></tr><tr><td><code>pink-bluegrey.css</code></td><td>Dark</td><td>pink, bluegrey, red</td></tr><tr><td><code>purple-green.css</code></td><td>Dark</td><td>purple, green, red</td></tr></tbody></table>

{% hint style="info" %}
We use a pre-built theme for this tutorial, but you can customize the DkGrid theming.
{% endhint %}

You can add the DkGrid styles in two ways: **importing the CSS file** directly in the `styles.scss` file or adding the entry in the **styles array** from the `angular.json` file.

### Importing the CSS file

Open the `styles.scss` file and add the following line:

{% code title="styles.scss" lineNumbers="true" %}

```scss
@import "@dk-grid-standard/angular/prebuilt-themes/indigo-pink.css";
```

{% endcode %}

### Adding the CSS file to the styles array

Open the `angular.json` file and add the following entry to the styles array:

{% code title="angular.json" lineNumbers="true" %}

```json
"styles": [
   "node_modules/@dk-grid-standard/angular/prebuilt-themes/indigo-pink.css",
   "src/styles.scss"
]
```

{% endcode %}

## DkGrid basic configuration

The next step is declaring a basic configuration to display some columns. Then, add the following code to `app.component.html` and `app.component.ts` files and remove the scaffolding code.

{% tabs %}
{% tab title="app.component.html" %}
{% code lineNumbers="true" %}

```markup
<dk-grid id="basic-conf-grid" height="500px"
  [datasource]="dkgDataSource">

    <dk-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350">
    </dk-grid-column>
    
    <dk-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150">
    </dk-grid-column>

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

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

    <dk-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="85">
    </dk-grid-column>

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

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

</dk-grid>
```

{% endcode %}
{% endtab %}

{% tab title="app.component.ts" %}
{% code lineNumbers="true" %}

```typescript
import { Component, OnInit } from '@angular/core';
import { DkGridDataSourceInput } from '@dk-grid-standard/angular';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  dkgDataSource: DkGridDataSourceInput<Partial<Employee>>;

  constructor() { }

  ngOnInit(): void {
    this.dkgDataSource = [{
        employeeId: '43c8a235-a95d-4e3e-bc30-f31ff33d490d',
        firstName: 'Dolores',
        lastName: 'Zieme',
        gender: 'female',
        age: 51,
        email: 'dolores.zieme@gmail.com',
        hireDate: new Date(2020, 7, 6)
    }, {
        employeeId: '0f1bc4f3-f746-489a-9c24-eb570a16e556',
        firstName: 'Celia',
        lastName: 'Mertz',
        gender: 'female',
        age: 55,
        email: 'celia.mertz@gmail.com',
        hireDate: new Date(2010, 5, 11)
    }, {
        employeeId: '88acf950-dee5-480a-a67e-1b0419c9a473',
        firstName: 'Jeffery',
        lastName: 'Renner',
        gender: 'male',
        age: 60,
        email: 'jeffery.renner@hotmail.com',
        hireDate: new Date(2016, 11, 22)
    }, {
        employeeId: '03a4e13f-98b4-4998-a40b-b66a832f327e',
        firstName: 'Annette',
        lastName: 'Satterfield',
        gender: 'female',
        age: 37,
        email: 'annette.satterfield@yahoo.com',
        hireDate: new Date(2019, 5, 2)
    }, {
        employeeId: 'a800045a-5127-4a5f-b93f-635f162ad376',
        firstName: 'Geraldine',
        lastName: 'Weimann',
        gender: 'female',
        age: 42,
        email: 'geraldine.weimann@hotmail.com',
        hireDate: new Date(2016, 10, 19)
    }] as Partial<Employee>[];
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Adding the <mark style="color:red;">`dk-grid`</mark> selector to the `app.component.html` file, we have created a new instance of type <mark style="color:green;">`DkGridComponent`</mark>.

### Column definitions

You can create columns using instances of type <mark style="color:green;">`DkGridColumnComponent`</mark> via templating.

{% hint style="info" %}
You can define columns in code as well
{% endhint %}

A basic column definition consists of:

<table><thead><tr><th width="198.3097476496784">Property name</th><th width="427.4285714285714">Description</th></tr></thead><tbody><tr><td><code>fieldName</code></td><td>The name of the field in the data source.</td></tr><tr><td><code>headerText</code></td><td>The visible label for the given column.</td></tr><tr><td><code>dataType</code></td><td>Data type could be <mark style="color:red;"><code>Text</code></mark>, <mark style="color:red;"><code>Numeric</code></mark>, <mark style="color:red;"><code>Date</code></mark> or <mark style="color:red;"><code>Binary</code></mark>.</td></tr></tbody></table>

{% hint style="info" %}
If you do not provide the <mark style="color:orange;">`width`</mark> property, as the hire column, the **default** value is **200**.

The <mark style="color:orange;">`width`</mark> value is given in **pixels**.
{% endhint %}

### Binding the Datasource

The DkGrid component defines an input property called <mark style="color:orange;">`datasource`</mark>. The type of the <mark style="color:orange;">`datasource`</mark> property is a union type called <mark style="color:green;">`DkGridDataSourceInput`</mark>.

{% code lineNumbers="true" %}

```typescript
// DkGridDataSourceInput definition:
type DkGridDataSourceInput<T extends object> = DataSource<T> | 
    DkDataSource<T> | DkGridDataSource<T> | 
    Observable<ReadonlyArray<T> | T[]> | 
    ReadonlyArray<T> | T[];
```

{% endcode %}

According to the <mark style="color:green;">`DkGridDataSourceInput`</mark> definition, a simple array is provided for this tutorial, adding five rows with a shape of a partial <mark style="color:green;">`Employee`</mark> interface.

### Displaying the output

If everything goes well, you will see an output similar to the following screenshot:

![DkGrid - Basic configuration output](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fihir3w6cEw5oUmCNPvsT%2Fgetting-started-basic-conf.png?alt=media\&token=e8e1641a-c87d-421b-9a29-3cf0b44b09b8)

## Exploring more functionalities

So far, we have provided the minimum number of properties. Now it is time to explore more functionalities and how easy it is to enable them.

### Enabling In-line Filters

The set of rows for this tutorial is minimal, but you probably load hundreds or even thousands of rows in an actual application.

Filtering rows is a must-have for any enterprise application. The DkGrid allows two types of filters:

1. **In-line Filters**. It displays a row under the column headers, allowing the user to type the search term for each column.
2. **Column Filters**. It will enable the user to enter more than one condition for each column.&#x20;

{% hint style="info" %}
DkGrid lets you customize both types of filters.
{% endhint %}

{% hint style="success" %}
We have enabled **In-line Filters** for this tutorial.
{% endhint %}

Open the file `app.component.html` and add the property <mark style="color:orange;">`allowRowFiltering`</mark> to the <mark style="color:green;">`DkGridComponent`</mark> instance. For the sake of brevity, we only show the <mark style="color:red;">`dk-grid`</mark> selector.

{% code title="app.component.html" lineNumbers="true" %}

```markup
<dk-grid id="basic-conf-grid" height="500px"
   allowRowFiltering
   [datasource]="dkgDataSource">
</dk-grid>
```

{% endcode %}

The following screenshot shows the result of allowing the row filter. We have highlighted the row under the headers with a red square. Moreover, we typed the letter ***d*** as a filter.

![Enabling In-line Filters](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FR3L16DcnjfLkV6fDs2IC%2Fgetting-started-row-filter.png?alt=media\&token=4f985037-5394-454b-818a-571fa3d29634)

### Enabling Sorting

For allowing the sort function for every desired column, you should enable sorting at the grid level, then at the column level.

Open the file `app.component.html` and add the following:

1. For the <mark style="color:red;">`dk-grid`</mark> selector, add the property <mark style="color:orange;">`allowSorting`</mark>.
2. And for every column you want to be sortable, add the property <mark style="color:orange;">`sortable`</mark>.

For the sake of brevity, we only show the <mark style="color:red;">`dk-grid`</mark>, and the target <mark style="color:red;">`dk-grid-column`</mark> selectors.

{% code title="app.component.html" lineNumbers="true" %}

```markup
<dk-grid id="basic-conf-grid" height="500px"
    allowRowFiltering
    allowSorting
    [datasource]="dkgDataSource">

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

    <dk-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        sortable>
    </dk-grid-column>
</dk-grid>
```

{% endcode %}

When you hover the mouse over the column header, you will see an arrow indicating that you can sort the DkGrid rows through this column.

If you click on the column header, you sort the DkGrid rows, and a white arrow indicates the sorting operation.

If you click more than one time:

1. The first click will order the data set in **ascending** way.
2. The second click will invert the order meaning that the data set is ordered in **descending** mode.
3. With the third click, the data set will not be ordered through the given column anymore.

{% hint style="info" %}
Sorting is applied one column at a time.
{% endhint %}

After applying the sorting properties, you will get something that looks like the following:

![Sorting functionality](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4ih2R216HZUxXPjxe9Sm%2Fgetting-started-sorting.png?alt=media\&token=d9353f41-f140-4512-8c8c-d6dd6038b52a)

## Other features

You can customize more features using a DkGrid component. In this section, we explore two more features regarding the UI.

### Material Elevation / Highlighting the odd rows

Since we base the DkGrid definition on Angular Material, you can add elevation to the DkGrid component.

When the user is working with a large data set, it is helpful to distinguish one row from the other. To accomplish it, add a flag at the level of the grid.

Open the file `app.component.html` and add the <mark style="color:orange;">`gridElevation`</mark> and <mark style="color:orange;">`striped`</mark> properties to the <mark style="color:red;">`dk-grid`</mark> selector.

{% code title="app.component.html" lineNumbers="true" %}

```markup
<dk-grid id="basic-conf-grid" height="500px"
    allowRowFiltering
    allowSorting
    striped
    gridElevation
    [datasource]="dkgDataSource">
</dk-grid>
```

{% endcode %}

Same as before, we only show the <mark style="color:red;">`dk-grid`</mark> selector for brevity.

Your DkGrid instance will look like the following output:

![Material Elevation and highlighted rows](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F3pPhITP3CgZTn9TrEmd3%2Fgetting-started-ui-features.png?alt=media\&token=b556ff52-8852-4720-b192-4abfd4f8efae)

{% hint style="info" %}
By default, adding the <mark style="color:orange;">`gridElevation`</mark> flag will show an elevation with a value of two.
{% endhint %}

{% hint style="info" %}
You can change the elevation, providing a number between **0** to **24**. You provide this value through a property called <mark style="color:orange;">`gridElevationValue`</mark>.
{% endhint %}

## Summary

In this tutorial, we learned how to define columns and bind some data for displaying it using a DkGrid instance. In addition, we have managed grid filtering and sorting and some UI features, including grid styling. But, of course, the best thing is how easy it is to enable these features.


# Grid Structure

This section describes the main parts of a DikeGrid instance and how you can customize this structure.

## Live example

{% hint style="success" %}
[Grid Structure](https://demos.dikesoft.com/dk-grid/grid-structure) live example.
{% endhint %}

## CSS Grid Layout

The DikeGrid uses the CSS Grid Layout specification to create its internal structure and display all the defined columns.&#x20;

As you know, the CSS Grid Layout specification uses column and row templates. That is why every part of the DikeGrid is called a row. Therefore, for instance, the Header is called the Header Row, the Content, Content Row, etc.

## Code for this section

You can read any sub-section in the order you want. However, we recommend reading all sub-sections as they appear.

The following initial configuration will increase in every sub-section.

{% tabs %}
{% tab title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [width]="gridProperties.width"
    [height]="gridProperties.height"
    
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text">
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text">
    </dike-grid-column>
</dike-grid>
```

{% endtab %}

{% tab title="grid-structure.component.ts" %}

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

import { Subscription } from 'rxjs';

import { DikeGridDataSourceInput } from '@dikesoft/data-grid';

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

import { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class GridStructureComponent implements OnInit, OnDestroy {

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;
  
  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

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

{% endtab %}
{% endtabs %}

### Complete code

{% tabs %}
{% tab title="structure.module.ts" %}

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

import { DikeDataGridModule, DIVISION_PANEL_GAP,
    DIVISION_PANEL_LINE_WIDTH, BORDER_WIDTH, BORDER_GAP,
    COLUMN_CONTEXT_MENU_WIDTH, COLUMN_CONTEXT_MENU_HEIGHT,
    LOADING_MESSAGE
} from '@dikesoft/data-grid';

import { SharedModule } from 'app/shared/shared.module';
import { structureRoutes } from 'app/modules/admin/structure/structure.routing';
import { GridStructureComponent } from 'app/modules/admin/structure/grid-structure/grid-structure.component';

@NgModule({
  declarations: [
    GridStructureComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild(structureRoutes),

    SharedModule,
    DikeDataGridModule
  ],
  providers: [
    { provide: DIVISION_PANEL_GAP, useValue: 21},
    { provide: DIVISION_PANEL_LINE_WIDTH, useValue: 3},
    { provide: BORDER_WIDTH, useValue: 3 },
    { provide: BORDER_GAP, useValue: 35 },
    { provide: COLUMN_CONTEXT_MENU_WIDTH, useValue: 700 },
    { provide: COLUMN_CONTEXT_MENU_HEIGHT, useValue: 550 },
    { provide: LOADING_MESSAGE, useValue: 'Loading rows of data, please wait...' }
  ]
})
export class StructureModule { }

```

{% endtab %}

{% tab title="grid-structure.component.html" %}

```markup
<div class="p-4 leading-6 text-secondary"><strong>Waiting Indicator message</strong></div>
<div class="p-4 flex flex-row flex-wrap justify-around items-center">
    <mat-form-field class="flex-auto w-80 mr-5">
        <mat-label>Custom message</mat-label>
        <input matInput
            type="text"
            [formControl]="customMessage">

        <mat-hint align="end">Text value</mat-hint>
        <mat-error *ngIf="customMessage.hasError('required')">Required</mat-error>
    </mat-form-field>
    <button mat-raised-button
        class="flex-none w-32 mr-5 mb-3"
        color="primary"
        [disabled]="!customMessage.valid"
        (click)="onDisplayWaitingIndicator()">Show Indicator
    </button>
    <button mat-raised-button
        class="flex-none w-32 mr-5 mb-3"
        color="primary"
        (click)="onHideWaitingIndicator()">Hide Indicator
    </button>
</div>

<dike-grid id="grid-structure" #grid="dkgGrid"
    [width]="gridProperties.width"
    [height]="gridProperties.height"

    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"
    
    [headerRowHeight]="gridProperties.headerHeight"
    [allowColumnContextMenu]="gridProperties.columnContextMenu"

    [allowRowFiltering]="gridProperties.allowRowFilter"
    [filterRowHeight]="gridProperties.filterRowHeight"

    [contentRowHeight]="gridProperties.contentRowHeight"

    allowRowGrouping
    [rowGroupingRowHeight]="gridProperties.rowGroupingHeight"
    [rowGroupingColumnHeight]="gridProperties.rowGroupingColumnHeight"
    [displayRowGroupingIndent]="gridProperties.displayRowGroupingIndent"
    [rowGroupingIndentWidth]="gridProperties.rowGroupingIndentWidth"

    allowSelection

    allowEdition
    [editionMode]="gridProperties.editionMode"
    [editionToolbar]="gridProperties.displayEditionToolbar"
    [editionToolbarPosition]="gridProperties.editionToolbarPosition"
    [editionToolbarAlignment]="gridProperties.editionToolbarAlignment"
    [editionToolbarRowHeight]="gridProperties.editionToolbarHeight"
    [editionToolbarItemHeight]="gridProperties.edtitionToolbarItemHeight"

    [allowPagination]="gridProperties.allowPagination"
    [paginationRowHeight]="gridProperties.paginationHeight"

    [allowColumnDragging]="gridProperties.allowColumnDragging"
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name">
        
        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text">
        </dike-grid-column>
    
        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text">
        </dike-grid-column>
        
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="110"
        groupable
        draggable
        panel="groupPanel">
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="85"
        groupable
        draggable
        panel="groupPanel">
    </dike-grid-column>

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

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

{% endtab %}

{% tab title="grid-structure.component.ts" %}

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

import { Subscription } from 'rxjs';
import { delay } from 'rxjs/operators';

import { DikeGridComponent, DikeGridDataSourceInput } from '@dikesoft/data-grid';

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

import { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class GridStructureComponent implements OnInit, OnDestroy {

  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;
  customMessage: FormControl;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

    this.customMessage = new FormControl('', Validators.required);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000).pipe(delay(2000));
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

  onDisplayWaitingIndicator(): void {
    this.dikeGrid.waitingIndicator.customMessageIndicator(this.customMessage.value as string);
  }

  onHideWaitingIndicator(): void {
    this.dikeGrid.waitingIndicator.hideWaitingIndicator();
  }

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

```

{% endtab %}
{% endtabs %}


# Grid Size

You can customize the width and the height of the DikeGrid instance.

## Grid width

By default, the DikeGrid width is equal to ***auto***. However, you can change this value by providing width's input property.

{% hint style="success" %}
You can also change this value by providing an Injection Token called <mark style="color:blue;">`GRID_WIDTH`</mark>.
{% endhint %}

## Grid height

By default, the DikeGrid height is equal to ***90vh***. However, you can change this value by providing height's input property.

{% hint style="success" %}
You can also change this value by providing an Injection Token called <mark style="color:blue;">`GRID_HEIGHT`</mark>.
{% endhint %}

## Changing the DikeGrid size

Let us give the DikeGrid the previous input properties, binding the related properties from the gridProperties object.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [width]="gridProperties.width"
    [height]="gridProperties.height">
</dike-grid>
```

{% endcode %}

Now, open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Size** section, type **80%** and **90vh** for the width and the height, respectively.

![Floating Configuration Panel - Size](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkFyJGYIG8hNLPwC7G2o4%2Fsize-panel-conf.png?alt=media\&token=2c338d96-5b0b-4c1e-bd6b-ed0dc167c262)

![Changing the DikeGrid size](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FMKMhedxDdQKZ7f1nQkTM%2Fgrid-structure-size-changed.png?alt=media\&token=e2f2b641-a08c-4522-92b9-6bf1fdf225ec)


# Header

Since the DikeGrid structure definition uses the CSS Grid Layout specification, as a convention, the Header is also called Header Row.

## Header Row

The Header Row of the DikeGrid is composed of all the column headers.

This initial setup generates the following output:

![Header Row](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fr39LH8QF6NOcNn67zJj8%2Fgrid-structure-default-header.png?alt=media\&token=c6b9ff78-43d5-4d5b-a9ac-b14f56598d3f)

As you can see, the Header Row occupies the entire horizontal extension of the DikeGrid instance.

The highlighted Header Row represents only one column definition in the CSS Grid Layout specification. Therefore, the DikeGrid CSS Grid Layout will have three template columns when defining columns in all panels of the DikeGrid.

{% hint style="info" %}
See the [Panels' structure](/fundamentals/grid-structure/panels) for more details.
{% endhint %}

Inside the Header Row comes the actual column headers definitions. The Header Row uses a nested CSS Grid Layout grid.

![Column Headers definition](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkvWq5ePFozpOZmipPrJ9%2Fgrid-structure-header-definition.png?alt=media\&token=602bbfdf-6345-4856-b3b6-c9a3509f55c8)

## Changing the Header Row height

By default, the column headers will have a height of **60 pixels**. However, you can change this height by providing a numeric value to the input property called <mark style="color:orange;">`headerRowHeight`</mark>.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [headerRowHeight]="gridProperties.headerHeight">
</dike-grid>
```

{% endcode %}

{% hint style="success" %}
You can also change the height of the Header by providing the Injection Token called <mark style="color:blue;">`HEADER_ROW_HEIGHT`</mark>.
{% endhint %}

In the previous code snippet, we added the <mark style="color:orange;">`headerRowHeight`</mark> property to the DikeGrid instance and was bound to the `gridProperties` object.

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Header** group, and type 120 in the **Header Height** textbox.

![Floating Configuration Panel - Header](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FOW3hsERRFr2jAdvOJZxI%2Fgrid-structure-header-panel-conf.png?alt=media\&token=51bda50a-ecb3-4be2-adf6-a31a745c5775)

![Changing the Header Row height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FPiWA0X0SSLp6isybgy1Q%2Fgrid-structure-change-header-height.png?alt=media\&token=83edafb8-6a89-4755-954d-cce477909a3d)

### Column Groups

Be aware that the **Header Row height** will be present in every level when grouping columns. Therefore, the total size of the DikeGrid Header will be the sum of every nested group.

Let us define a group named **Complete Name**. This group will have two child columns: **Name** and **Surname** columns.

{% code title="grid-structure.component.html" %}

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text">
        </dike-grid-column>
    </dike-grid-column>
</dike-grid>
```

{% endcode %}

![Header Row Height - Column Groups](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FjEjwN859qhRFo4IKwFGA%2Fgrid-structure-change-header-height-groups.png?alt=media\&token=c535f81a-5755-4042-9f46-8f4b49b4d1fd)

{% hint style="success" %}
For further details about groups, see [Column Grouping](/columns/column-grouping).
{% endhint %}


# Row Filter

Since the DikeGrid structure definition uses the CSS Grid Layout specification, as a convention, the Row Filter is also called Row-Filter Row.

## Row-Filter Row

The Row-Filter Row of the DikeGrid enables a textbox for every defined column. To show the Row-Filter Row, we have to provide an input property called <mark style="color:orange;">`allowRowFiltering`</mark>.

Let us bind the related property from the `gridProperties` object to display the Row-Filter Row.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [allowRowFiltering]="gridProperties.allowRowFilter">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Filtering** group, and click on the **Row Filter** checkbox.

![Floating Configuration Panel - Filtering](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F2OtUPdlhx9h1L3Hy5gjK%2Fgrid-structure-row-filter-panel-conf.png?alt=media\&token=8812804b-b5eb-492d-b07e-38078fc75f7f)

The following screenshot shows how the Row-Filter Row looks:

![Row Filter](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FcjCuolufKHOjoMTZ8iYl%2Fgrid-structure-row-filter.png?alt=media\&token=0ef889b4-7689-4978-a366-0d98a60b00db)

Same as the Header, the Row-Filter Row occupies the full horizontal extension of the DikeGrid instance.

The highlighted Row-Filter Row represents only one column definition in the CSS grid layout specification. Therefore, the DikeGrid CSS grid layout will have three template columns when defining columns in all panels of the DikeGrid.

{% hint style="info" %}
See the [Panels' structure](/fundamentals/grid-structure/panels#content-panels) for more details.
{% endhint %}

Inside the Row-Filter Row comes the actual column filters definitions. The Row-Filter Row uses a nested CSS Grid Layout grid.

![Row Filters definitions](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FmJttlxQ5CKrGDGhdxHEV%2Fgrid-structure-row-filter-definition.png?alt=media\&token=045c9459-7b11-46b8-99d3-047f9cea3a7a)

## Changing the Row-Filter Row height

By default, the Row-Filter Row has a height of **55 pixels**. Nevertheless, you can change this height by providing a numeric value to the input property called <mark style="color:orange;">`filterRowHeight`</mark>.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [allowRowFiltering]="gridProperties.allowRowFilter"
    [filterRowHeight]="gridProperties.filterRowHeight">
</dike-grid>
```

{% endcode %}

{% hint style="success" %}
You can also change the height of the Filter-Row Row by providing the Injection Token called <mark style="color:blue;">`FILTER_ROW_HEIGHT`</mark>.
{% endhint %}

In the previous code snippet, we added the <mark style="color:orange;">`filterRowHeight`</mark> property to the DikeGrid instance and was bound to the `gridProperties` object.

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Filtering** group, and type 80 in the **Row-Filter Row Height** textbox.

![Changing the Row Filter Row height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FpP3O0iBiy3MmYylijj80%2Fgrid-structure-row-filter-change.png?alt=media\&token=195f6815-8668-441d-a3e3-fca5bd198ca2)


# Rows

Since the DikeGrid structure definition uses the CSS Grid Layout specification, as a convention, every row is also called Content Row.

## Content Rows

Content Rows are composed of cells. Structurally speaking, a column definition constructs a cell definition.

Content Rows extend the entire DikeGrid width. The following output shows some Content Rows:

![Content Rows](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FUeMK2q6R0kwiCqB2Jyen%2Fgrid-structure-content-rows.png?alt=media\&token=ea0f4285-7b23-40c8-9e65-352dada5e976)

The highlighted Content Rows have one column definition in the CSS Grid Layout specification. Therefore, the DikeGrid CSS Grid Layout will have three template columns when defining columns in all panels of the DikeGrid.

{% hint style="info" %}
See the [Panels' structure](/fundamentals/grid-structure/panels) for more details.
{% endhint %}

Inside the Content Rows come the actual cell definitions. Content Rows use a nested CSS Grid Layout grid.

![Cell definitions](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FGfhMsLdDFXLsvZysZGcb%2Fgrid-structure-cell-definitions.png?alt=media\&token=cb66202d-6d7d-48d8-96fa-707df89446e9)

## Changing the Content Row height

By default, Content Rows have a height of **50 pixels**. However, you can change this height by providing a numeric value to the input property called <mark style="color:orange;">`contentRowHeight`</mark>.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [contentRowHeight]="gridProperties.contentRowHeight">
</dike-grid>
```

{% endcode %}

{% hint style="success" %}
You can also change the height of the Content Rows by providing the Injection Token called <mark style="color:blue;">`CONTENT_ROW_HEIGHT`</mark>.
{% endhint %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Rows** group, and type 100 in the **Content Row Height** textbox.

![Floating Configuration Panel - Rows](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FvZFuFLhgPkpXsleW5J9q%2Fgrid-structure-type-content-row-height.png?alt=media\&token=861d95e7-df97-45d2-866e-8133179d441a)

![Changing the Content Rows height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F02dFMjigkhssGDVuCZ52%2Fgrid-structure-change-content-row-height.png?alt=media\&token=860beb26-9ee3-40f1-a9e1-c333e3a0fe2f)


# Panels

Panels are containers to accommodate columns.

## Content Panels

There are three Content Panels in the DikeGrid structure definition. The Content Panels are **left**, **center**, and **right** panels. Every Content Panel can accommodate any number of columns, but only the center panel will be scrollable.

Let us rearrange and create the following columns:

1. First, add the **Employee Id** column in the **left** panel.
2. Then, add the **Name**, **Surname**, **Gender**, **Age**, and **Email** columns to the **center** panel.
3. Lastly, add the **Hire Date** column to the **right** panel.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure">
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350"
        panel="leftPanel">
    </dike-grid-column>

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text">
    </dike-grid-column>

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

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

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

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

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

</dike-grid>
```

{% endcode %}

The following output shows the previous column definition:

![Content Panels](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FCCxJi6fm2U5lTSltWUj3%2Fgrid-structure-content-panels.png?alt=media\&token=4e1174f4-ee1d-485a-91d8-26623a7c2108)

{% hint style="info" %}
You can define columns or column groups in a Content Panel.
{% endhint %}

As you can see, columns in the left and right panels are visible all the time.

Highlighting the cell definitions for the three panels, we can see that the Content Row is split into three template columns according to the CSS Grid Layout specification.

![Cell definitions for a Content Row](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FRIASuB51uQLQvU4EllNC%2Fgrid-structure-content-panels-conent-rows.png?alt=media\&token=2340e750-889e-4b87-8af4-c64960c75dd6)

{% hint style="danger" %}
Be aware of left and right panels' width. Since these panels are not scrollable, if the left and right panels exceed the grid's width, the center panel will not be visible.
{% endhint %}

### Panel division line

A division line separates two adjacent panels when creating columns in the left or right panels. Every line belongs to the left and right panels, respectively.

{% hint style="info" %}
All resizable columns in a panel change their width evenly when resizing the panel. For more details, see [Column Sizing](/columns/column-sizing#dragging-the-panel-division-line).
{% endhint %}

#### Space between two panels

There is a space between two adjacent panels. Then, the DikeGrid draws the division line at the center of this space.&#x20;

By default, the space between two adjacent panels is **13 pixels** in width. However, you can change the space width by providing an Injection Token called <mark style="color:blue;">`DIVISION_PANEL_GAP`</mark>.

#### Panel division line width

The division line is a **pixel width**. Same as before, you can change this value by providing an Injection Token called <mark style="color:blue;">`DIVISION_PANEL_LINE_WIDTH`</mark>.

The following code snippet shows custom values for the mentioned Injection Tokens.

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

```typescript
@NgModule({
  providers: [
    { provide: DIVISION_PANEL_GAP, useValue: 21},
    { provide: DIVISION_PANEL_LINE_WIDTH, useValue: 3}
  ]
})
export class StructureModule { }

```

{% endcode %}

The following screenshot shows the previous module configuration.

![Custom panel division line](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkVgUZU7arnUMIiDoRqnG%2Fgrid-structure-content-panels-division-line.png?alt=media\&token=66fbfff1-8e63-4877-8bc6-ad2b31b7afe6)

As we can see in the previous screenshot, the panel division line is thicker, and the space between panels is larger.

## Group panel

Like any other panel in the DikeGrid definition, the group panel accommodates all the columns to group the DikeGrid data rows.

Let us allow row grouping in the DikeGrid instance to see how the group panel looks.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    allowRowGrouping>
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can enable row grouping only at the creation phase of the DikeGrid.
{% endhint %}

The following screenshot shows the group panel. As you can see, the group panel is at the ***top*** of the column headers.

![Group Panel](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FAvFe4cKWF28BN8iS2wjf%2Fgrid-structure-group-panel.png?alt=media\&token=3fc16a5a-823b-47ee-996e-fd7df8f7ffb2)

Rearrange the **Age** column to display it in the group panel.&#x20;

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    allowRowGrouping>
    
    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="85"
        groupable
        panel="groupPanel">
    </dike-grid-column>
</dike-grid>
```

{% endcode %}

After making the **Age** column **groupable** and creating it in the **group panel**, the output looks like the following:

![A DikeGrid instance group by Age column](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FmF6aKfvrfvULUc2HLA3n%2Fgrid-structure-group-panel-age-column.png?alt=media\&token=8fc2a2f4-a4d9-4efb-9ced-2ef6f5a6653e)

{% hint style="warning" %}
You can define **data** columns in the **group panel**, only; not column groups. For more details, see the Row Grouping section.
{% endhint %}

### Changing the group panel height

By default, the group panel has **71 pixels** of height. You can change this value by providing an input property called <mark style="color:orange;">`rowGroupingRowHeight`</mark>.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    allowRowGrouping
    [rowGroupingRowHeight]="gridProperties.rowGroupingHeight">
</dike-grid>
```

{% endcode %}

In the previous code snippet, we added the <mark style="color:orange;">`rowGroupingRowHeight`</mark> property to the DikeGrid instance and was bound to the `gridProperties` object.

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Row Grouping** section, and type 100 in the **Row-Grouping Row Height** textbox.

![Floating Configuration Panel - Row Grouping](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F1aY24fNVkOduLzYve2x5%2Fgrid-structure-group-panel-panel-conf.png?alt=media\&token=d0cf6ce4-37e8-42af-828a-a47d30bef606)

![Changing the Group Panel height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FJVIGCQBcyzVcJjDKKbJL%2Fgrid-structure-group-panel-change-height.png?alt=media\&token=fec0589f-9a85-43b5-84d5-65e6f5de8934)

{% hint style="success" %}
You can also change the group panel height by providing an Injection Token called <mark style="color:blue;">`ROW_GROUPING_ROW_HEIGHT`</mark>.
{% endhint %}

### Changing the height of the columns in the group panel

By default, the columns contained in the group panel have a height of **36 pixels**. However, you can change it by providing a value for the input property called <mark style="color:orange;">`rowGroupingColumnHeight`</mark>.

Let us bind the `gridProperties` object to the <mark style="color:orange;">`rowGroupingColumnHeight`</mark> input property.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    allowRowGrouping
    [rowGroupingRowHeight]="gridProperties.rowGroupingHeight"
    [rowGroupingColumnHeight]="gridProperties.rowGroupingColumnHeight">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) go to the **Row Grouping** section, and type 80 in the **Columns Height** textbox.

![Height of the columns in Group Panel](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FVDMqHDEbyfw09WtzyQVA%2Fgrid-structure-group-panel-column-height-change.png?alt=media\&token=bf03f46b-c9fa-4735-b8a8-dc57f70f74d8)

{% hint style="success" %}
You can also change the height of the columns by providing an Injection Token called <mark style="color:blue;">`ROW_GROUPING_COLUMN_HEIGHT`</mark>.
{% endhint %}

### Changing the Row-Grouping indent width

When you open a group, you will see an indent between the most left side of the DikeGrid and the first column.

The Row-Grouping indent is visible by default. However, you can hide it if you want. You can show or hide this indent by providing an input property called <mark style="color:orange;">`displayRowGroupingIndent`</mark>.

{% hint style="success" %}
You can also change the indent width by providing an Injection Token called <mark style="color:blue;">`ROW_GROUPING_INDENT`</mark>.
{% endhint %}

By default, this indent is **25 pixels** in width. Nevertheless, you can change the indent width by providing an input property called <mark style="color:orange;">`rowGroupingIndentWidth`</mark>.

{% hint style="success" %}
You can also change the indent width by providing an Injection Token called <mark style="color:blue;">`ROW_GROUPING_INDENT_WIDTH`</mark>.
{% endhint %}

Let us bind the related properties from the `gridProperties` object.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    allowRowGrouping
    [rowGroupingRowHeight]="gridProperties.rowGroupingHeight"
    [rowGroupingColumnHeight]="gridProperties.rowGroupingColumnHeight"
    [displayRowGroupingIndent]="gridProperties.displayRowGroupingIndent"
    [rowGroupingIndentWidth]="gridProperties.rowGroupingIndentWidth">
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="110"
        groupable
        draggable
        panel="groupPanel">
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="85"
        groupable
        draggable
        panel="groupPanel">
    </dike-grid-column>
    
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can remove the columns from the **group panel** because the <mark style="color:orange;">`draggable`</mark> input property is bound to the **Gender** and **Age** columns.
{% endhint %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) go to the **Row Grouping** section, and type 100 in the **Indent Width** textbox. You can uncheck the **Row-Grouping Indent** checkbox to hide the Row-Grouping indent.

![Changing the row grouping indent width](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FbXjIpwEpS7ZUmWvSUQZY%2Fgrid-structure-indent-change.png?alt=media\&token=d1a9fb44-c656-44a7-9ce6-95de84339816)

To apply an indent to every Content Row depends on the row belonging level.

We grouped the DikeGrid rows by two columns in the previous configuration: **Gender** and **Age**.

This configuration generates an output of **three levels**:

1. **Level zero.** This level is the first group, which corresponds to the **Gener** column. At this level, the indent is not visible.
2. **Level one.** This level is the second group, which corresponds to the **Age** column. At this level, the indent is 100 pixels in width.
3. **Level three.** This level corresponds to the data itself. At this level, the indent is 255 pixels in width.

{% hint style="info" %}
The indent is equal to ***indent width \* level*** ***+ 55 pixels*** for a nested data row.

The 55 pixels in width corresponds to the group icon used to open and close the group.
{% endhint %}


# Gutters

Gutters are the spaces between columns and rows. The DikeGrid definition could or could not draw a line at the center of these spaces.

## Horizontal Gutters

Horizontal Gutters are the vertical space between rows, and it has **3 pixels** of height. The DikeGrid definition always draws a line of one-pixel width at the center of this vertical space.

{% hint style="info" %}
Neither the vertical space nor the horizontal line is customizable in size.
{% endhint %}

## Vertical Gutters

Vertical Gutters are the horizontal space between columns. By default, Vertical Gutters have **5 pixels** in width.&#x20;

You can provide an input flag called <mark style="color:orange;">`verticalRowLines`</mark> to the DikeGrid, and it could draw a line of one-pixel width at the center of this horizontal space.

{% hint style="info" %}
Neither the horizontal space nor the vertical line is customizable in size.
{% endhint %}

Let us bind the related property from the `gridProperties` object to display the vertical lines per row.&#x20;

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [verticalRowLines]="gridProperties.verticalRowLines">
</dike-grid>
```

{% endcode %}

Now, open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Appearance** section, and enable the **Vertical Row Lines** checkbox.

![Floating Configuration Panel - Appearance](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FPLCFDpJj3qDGOZjCXAVq%2Fgrid-structure-vertical-row-lines-enabled.png?alt=media\&token=4a6837bb-eb98-4d2e-9a66-8d696401b547)

You can see the vertical lines per row in the following screenshot.

![Vertical lines per row](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FuyhY9X3y7eiSilanwqsL%2Fgrid-structure-vertical-row-lines.png?alt=media\&token=a81bb90d-40c3-4d5d-92c8-45e28d7bd36e)

### Who does the Vertical Gutters own?

By definition, columns own the Vertical Gutters:

1. Columns in the left, center or group panels own the right-gutter. Only the first visible column holds the left-gutter as well.
2. Columns in the right panel own the left-gutter. Only the last visible column holds the right-gutter as well.

{% hint style="info" %}
Vertical Gutter owning is important for **resizing** and **dragging** purposes.
{% endhint %}


# Edition Toolbar

Since the DikeGrid structure definition uses the CSS Grid Layout specification, as a convention, the Edition Toolbar is also known as Edition Toolbar Row.

## Edition Toolbar Row

When the DikeGrid allows editing rows, the DikeGrid enables some options to manage the edited rows.

Let us **allow edition** and bind the **edition mode** input property to the `gridProperties` object to show the Edition options.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    allowEdition
    [editionMode]="gridProperties.editionMode">
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can **allow edition** only at the creation phase of the DikeGrid.
{% endhint %}

Now, you could toggle the DikeGrid between ***view mode*** and ***edition mode***. Thus, open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Edition** section, and click on the **Edition mode** checkbox.

![Floating Configuration Panel - Edition](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F02z0J0RWkndtL2X7T3di%2Fgrid-structure-edition-panel-conf.png?alt=media\&token=d32fc072-66ce-4fec-b341-4f4f2827a466)

Once the DikeGrid is in edition mode, you can access the Edition options by default through a contextual menu at the left of the first visible column header.

![Edition options](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fuj25JdPmFrUINE2yTf9T%2Fgrid-structure-edition-options-menu.png?alt=media\&token=2519bff3-ac87-4792-9806-1444d1288865)

However, a DikeGrid instance could display those options through a toolbar. Hence, to show the Edition Toolbar Row, provide the input flag called <mark style="color:orange;">`editionToolbar`</mark>. Then, let us bind the related property from the `gridProperties` object to show the Edition Toolbar Row.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    allowSelection
    allowEdition
    [editionMode]="gridProperties.editionMode"
    [editionToolbar]="gridProperties.displayEditionToolbar">
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
The previous definition allows selection by providing an input flag called <mark style="color:orange;">`allowSelection`</mark>. Thus, we can see all the edition options.
{% endhint %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), and click on the **Edition Toolbar** checkbox.

![Edition Toolbar Row](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FUVDiNDkswDy6TOPnypMn%2Fgrid-structure-edition-toolbar.png?alt=media\&token=86fac4d5-60b4-4582-aef5-7f330ad28c0f)

You can now access all the edition options through the Edtion Toolbar shown at the ***bottom*** of the content rows.

### Edition Toolbar position

The DikeGrid could display the Edition Toolbar at the ***bottom*** of the content rows or the ***top*** of the column headers.

{% hint style="info" %}
By default, the DikeGrid shows the Edition Toolbar at the ***bottom*** of the content rows
{% endhint %}

Let us bind the related property from the `gridProperties` object to change the Edition Toolbar position.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [editionToolbarPosition]="gridProperties.editionToolbarPosition">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Edition** group and, select the ***top*** option.

![Edition Toolbar Row at the top of the column headers](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FCZGTpgdtQWiafZwx7gmE%2Fgrid-structure-edition-toolbar-top.png?alt=media\&token=2195c6ec-eba8-45d1-b7c0-cac3eb4eb969)

### Edition Toolbar alignment

The Edition Toolbar could align its options at the **left**, **center**, or **right**.

{% hint style="info" %}
The default options alignment is at the ***left***.
{% endhint %}

Bind the related property from the `gridProperties` object to change the Edition Toolbar alignment.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [editionToolbarAlignment]="gridProperties.editionToolbarAlignment">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Edition** group, and change the Edition Toolbar's alignment by clicking on the ***center*** option.

![Edition Toolbar with center alignment](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FAGuP7ar9GXF8TXLN3gxF%2Fgrid-structure-edition-toolbar-center.png?alt=media\&token=c21f68f0-4794-440a-bfc5-afef9f117cee)

## Changing the Edition Toolbar Row Height

The Edition Toolbar Row is **55 pixels** in height. However, you can change this value by providing an input property named <mark style="color:orange;">`editionToolbarRowHeight`</mark>.

Let us bind the related property from the `gridProperties` object to change the Edition Toolbar Row height.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [editionToolbarRowHeight]="gridProperties.editionToolbarHeight">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Edition** group, and type 100 in the **Toolbar Row Height** textbox.

![Changing the Edition Toolbar Row height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FarNa8M21e4GYkzdFlpvs%2Fgrid-structure-edition-toolbar-change-height.png?alt=media\&token=7d416b1f-d0b0-4af2-92f4-64215dfa4d4d)

{% hint style="success" %}
You can also change the Edition Toolbar Row height by providing an Injection Token called <mark style="color:blue;">`EDITION_TOOLBAR_ROW_HEIGHT`</mark>.
{% endhint %}

## Changing the Edition Toolbar Items height

Edition Toolbar Items are **42 pixels** in height. Nevertheless, you can change this height by providing an input property called <mark style="color:orange;">`editionToolbarItemHeight`</mark>.

Let us bind the related property from the `gridProperties` object to change the Edition Toolbar Items height.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [editionToolbarItemHeight]="gridProperties.edtitionToolbarItemHeight">
</dike-grid>
```

{% endcode %}

Now, open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Edition** group, and type 60 in the **Toolbar Item Height** textbox.

![Changing the Edition Toolbar Items height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fgyk9TcvXRIJdbq8cnDNE%2Fgrid-structure-edition-toolbar-change-item-height.png?alt=media\&token=91a78e00-0a30-40a5-ae1e-34063e26ff88)

{% hint style="success" %}
You can also change the Edition Toolbar Items height by providing an Injection Token called <mark style="color:blue;">`EDITION_TOOLBAR_ITEM_HEIGHT`</mark>.
{% endhint %}


# Paginator

Since the DikeGrid structure definition uses the CSS Grid Layout specification, as a convention, the paginator is also called the Pagination Row.

## Pagination Row

The Pagination Row shows the paginator when you enable pagination in the DikeGrid. Bind the related property from the `gridProperties` object to allow pagination.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [allowPagination]="gridProperties.allowPagination">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Pagination** group, and enable pagination.&#x20;

![Floating Configuration Panel - Pagination](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4rVJTuGq2Tse1P1FPZWE%2Fpagination-panel-conf.png?alt=media\&token=47cd5555-0451-4a04-95c1-b5b28a3ec573)

The previous configuration generates the following output:

![Pagination Row](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Ft54Botn1Fkojyk7LT8U3%2Fgrid-structure-paginator.png?alt=media\&token=ecfbba86-7455-48bc-a277-03164983dab3)

{% hint style="info" %}
The DikeGrid always shows the Pagination Row at the ***bottom*** of the content rows.
{% endhint %}

### Changing the Pagination Row height

The Pagination Row is **64 pixels** in height. Nevertheless, you can change this height by providing an input property called <mark style="color:orange;">`paginationRowHeight`</mark>.

Let us bind the corresponding property from the `gridProperties` object to change the Pagination Row height.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [allowPagination]="gridProperties.allowPagination"
    [paginationRowHeight]="gridProperties.paginationHeight">
</dike-grid>
```

{% endcode %}

Now, open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Pagination** group, and type 100 in the **Pagination Row Height** textbox.

![Changing the Pagination Row height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FAB3fKxfqcPqRH0sp4dB5%2Fgrid-structure-pagination-row-change.png?alt=media\&token=8c2280e9-819e-4411-ad10-2be3b83f9679)

{% hint style="success" %}
You can also change the Pagination Row height by providing an Injection Token called <mark style="color:blue;">`PAGINATION_ROW_HEIGHT`</mark>.
{% endhint %}


# Borders

The DikeGrid itself draws a border through its primary container. However, Borders concept is essential for column moving.

## DikeGrid Border

When you disable the material elevation for the DikeGrid, it draws a line in its primary internal container.

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Appearance** section, and uncheck the **MatElevation** checkbox.

![Floating Configuration Panel - Appearance](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FPLCFDpJj3qDGOZjCXAVq%2Fgrid-structure-vertical-row-lines-enabled.png?alt=media\&token=4a6837bb-eb98-4d2e-9a66-8d696401b547)

![DikeGrid Border](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FqptdOlB0PMR4U6h72Uu0%2Fgrid-structure-border.png?alt=media\&token=04a2f3c3-973e-427a-85a5-4505a074ca67)

{% hint style="info" %}
By default, the border is **one-pixel** in width.
{% endhint %}

### Border width

You can change the border width by providing an Injection Token called <mark style="color:blue;">`BORDER_WIDTH`</mark>.

Let us give the DikeGrid instance this Injection Token at the module level.

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

```typescript
@NgModule({
  providers: [
    { provide: BORDER_WIDTH, useValue: 3 }
  ]
})
export class StructureModule { }

```

{% endcode %}

![DikeGrid with a thicker border.](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FqHE1hj7QxkAyTXL7LjZK%2Fgrid-structure-thicker-border.png?alt=media\&token=563b2981-ee96-49bd-8938-8605b6b7052d)

## Borders for column moving

If we inspect the DikeGrid instance through the Chrome DevTools, we see the following structure:

![DikeGrid Border Gap](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FRtrMUbYz9e5MJxve20JC%2Fgrid-structure-border-gap.png?alt=media\&token=03c7f98a-e6aa-47dc-abb2-058adddc75f7)

The inner container highlighted with yellow is the primary container for the DikeGrid. However, there is another outer container highlighted with pink color.

The outer container creates a space between the DikeGrid definition and its external boundary. When a column moves and tries to add to the left or right panel, this space draws a line with the accent color.

{% hint style="info" %}
Internally, the space between the DikeGrid and its external boundary is called **Border Gap**.
{% endhint %}

To see how the DikeGrid draws this line, let us allow column dragging for the DikeGrid instance.

{% hint style="success" %}
Remember that **Age** and **Gender** columns are draggable, but the DikeGrid instance that contains them must allow column dragging. See the **Column Moving** section for more details.
{% endhint %}

Let us bind the related property from the `gridProperties` object to allow column dragging.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [allowColumnDragging]="gridProperties.allowColumnDragging">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Dragging** section, and check the **Allow Column Dragging** checkbox.

![Floating Configuration Panel - Dragging](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fv8NyyoupJ0lcSqptEayo%2Fdragging-panel-conf.png?alt=media\&token=982a2a66-6959-4cc4-8515-2cbcd9d9beca)

Now you can grab the **Gender** column, for example, and drag it to the left or right panel.

![DikeGrid - Left Border](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FVsYQpIXHn34TsDqunzAV%2Fgrid-structure-border-drawn.gif?alt=media\&token=6e8d5db9-c851-48fc-a5a0-a8c0055b734a)

See how the column icon changes to a ***pin icon***, indicating that the user will add the column to the **left panel**.

{% hint style="info" %}
The drawn line is **5 pixels** in width. Nevertheless, if the provided border width exceeds 5 pixels, the drawn line will be the same as the border.
{% endhint %}

### Changing the Border Gap width

The Border Gap has **15 pixels** in width by default. However, you can reduce or increase this gap by providing an Injection Token called <mark style="color:blue;">`BORDER_GAP`</mark>.

Let us give the DikeGrid instance this Injection Token at the module level.

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

```typescript
@NgModule({
  providers: [
    { provide: BORDER_WIDTH, useValue: 3 },
    { provide: BORDER_GAP, useValue: 35 }
  ]
})
export class StructureModule { }
```

{% endcode %}

We gave the DikeGrid an exaggerated value of **35 pixels** to see the difference.

![Border Gap with 35 pixels in width](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FdRodhExgWi1vkTL608Ac%2Fgrid-structure-border-gap-changed.png?alt=media\&token=a6b2a26e-55a6-41eb-b431-30980e2b4c77)

{% hint style="warning" %}
Be aware that the drawn line when a column is dragging must fit in the Border Gap.
{% endhint %}


# Column Context Menu

Every column displays a context menu, and this has a default size. You can customize the width and height of this context menu or avoid displaying it.

## Context Menu

By definition, every column will show a context menu. This context menu contains two sub-options by default: **Column Chooser** and **Column Pinning**.

### Column Chooser

The Column Chooser will display all the columns in the DikeGrid in order and as a tree. If there are no nested columns, the Column Chooser will show only a list of columns. Through the Column Chooser, you can make a column visible or not.

![Column Context Menu - Column Chooser](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F6lUvYdDOxhv8UnPZOpL6%2Fgrid-structure-context-menu-column-chooser.gif?alt=media\&token=627e603a-7bb5-4eca-a4c8-db14dedf840f)

### Column Pinning

This sub-option will show all the possible panels to drag a column.

![Column Context Menu - Column Pinning](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4nID3QWV6ywTCFSJlrRb%2Fgrid-structure-context-menu-column-pinning.png?alt=media\&token=eb502d55-c93f-48c0-b88d-0111aee2cb6f)

{% hint style="success" %}
See the [Column Pinning](#column-pinning) section for more details.
{% endhint %}

There are two more sub-options: **Filtering** and **Sorting**. These options will be available when you make columns filterable and allow sorting, respectively.

{% hint style="info" %}
You can close the context menu by clicking on the **close** icon at the upper-right corner or pressing the **ESC** key.
{% endhint %}

## Suppress the Column Context Menu

You can avoid displaying the context menu for a specific column, all columns in one particular DikeGrid definition, or all DikeGrid instances.

### Suppress at the column level

To suppress the context menu at the column level, you must provide an input property called <mark style="color:orange;">`displayMenu`</mark>.

Give the **Employee Id** column this input property a **false** value. Then, when you hover the column header of the **Employee Id** column, you will not see the context menu icon.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure">
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350"
        panel="leftPanel"
        displayMenu="false">
    </dike-grid-column>
</dike-grid>
```

{% endcode %}

### Suppress at the DikeGrid level

If you do not want the columns of a specific DikeGrid instance to display its context menu, you must provide an input property called <mark style="color:orange;">`allowColumnContextMenu`</mark>.

Let us bind the related property from the `gridProperties` object to suppress the context menu for all defined columns.

{% code title="grid-structure.component.html" %}

```markup
<dike-grid id="grid-structure"
    [allowColumnContextMenu]="gridProperties.columnContextMenu">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel), go to the **Header** section, and uncheck the **Column Context Menu** checkbox.

![Floating Configuration Panel - Header](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FOW3hsERRFr2jAdvOJZxI%2Fgrid-structure-header-panel-conf.png?alt=media\&token=51bda50a-ecb3-4be2-adf6-a31a745c5775)

Now, you will not see the context menu icon for all columns.

### Suppress for all DikeGrid instances

To suppress the context menu in all DikeGrid instances, you must provide a <mark style="color:red;">`false`</mark> value through the Injection Token called <mark style="color:blue;">`COLUMN_CONTEXT_MENU`</mark>.

## Context Menu width and height

By default, the context menu is **345 pixels** in width and **490 pixels** in height. However, you can modify these values by providing the related Injection Tokens, <mark style="color:blue;">`COLUMN_CONTEXT_MENU_WIDTH`</mark> and <mark style="color:blue;">`COLUMN_CONTEXT_MENU_HEIGHT`</mark>, respectively.

Let us inject new values through the mentioned Injection Tokens at the module level.

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

```typescript
@NgModule({
  providers: [
    { provide: COLUMN_CONTEXT_MENU_WIDTH, useValue: 700 },
    { provide: COLUMN_CONTEXT_MENU_HEIGHT, useValue: 550 }
  ]
})
export class StructureModule { }

```

{% endcode %}

![Column Context Menu - Changing the width and height](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FIcXtbp6jaoqYfqjTHH77%2Fgrid-structure-context-menu-size-changed.png?alt=media\&token=92189c1c-8c8d-4d65-be62-350aaa406b31)

As you can see, we have provided exaggerated values for width and height to spot the difference.


# Waiting Indicator

When the DikeGrid is waiting or processing rows of data, it shows a message. You can customize this message.

The DikeGrid definition overlaps a <mark style="color:red;">`div`</mark> element over the primary DikeGrid container, as you can see in the following screenshot. The yellow highlighted <mark style="color:red;">`div`</mark> is the primary DikeGrid container.

![Waiting Indicator div element](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FEjXR8SVxzOoXCVjpXEO1%2Fgrid-structure-waiting-indicator.png?alt=media\&token=7f47ffb1-42d2-4f8b-b68f-6d8ff4b2acd7)

## Loading Indicator

When the DikeGrid instance is waiting for the rows of data to display, it blocks its UI and shows a message. The message indicates this waiting.

Let us add a delay when the SampleData service loads the rows of data to see the displayed message.

{% code title="grid-structure.component.ts" %}

```typescript
ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000).pipe(delay(2000));
}
```

{% endcode %}

![Loading Indicator](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkHsUjbIfmITRvUrZULKm%2Fgrid-structure-loading-indicator.png?alt=media\&token=f6b26d7e-897a-4ac9-81f9-491b5ec8c6a9)

You can customize the displayed message by providing an Injection Token called <mark style="color:blue;">`LOADING_MESSAGE`</mark>.

Let us give the module this value for the above Injection Token.

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

```typescript
@NgModule({
  providers: [
    { provide: LOADING_MESSAGE, useValue: 'Loading rows of data, please wait...' }
  ]
})
export class StructureModule { }

```

{% endcode %}

![Loading Indicator - Custom Message](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FTjsfQhSfUIj2iNtHA63j%2Fgrid-structure-loading-indicator-custom-msg.png?alt=media\&token=070a2d04-7c46-431d-9e3d-8d4f319b5eb1)

## Processing Indicator

When the DikeGrid instance is processing an edition operation, it blocks its UI and shows a processing message.

{% hint style="success" %}
You can customize the displayed message by providing an Injection Token called <mark style="color:blue;">`PROCESSING_MESSAGE`</mark>.
{% endhint %}

## Displaying the indicator using the API

When you query a <mark style="color:green;">`DikeGridComponent`</mark> instance from the view, you can access a property called `waitingIndicator` of type <mark style="color:green;">`DikeGridWaitingIndicator`</mark>.

{% hint style="success" %}
See the [<mark style="color:green;">`DikeGridWaitingIndicator`</mark>](/reference/dkgrid-api/dkgridwaitingindicator) definition for more details.
{% endhint %}

You can show the Waiting Indicator imperatively invoking the methods defined in the `waitingIndicator` property.

Let us modify the code to allow entering a custom message and display it.

{% tabs %}
{% tab title="grid-structure.component.html" %}

```markup
<div class="p-4 leading-6 text-secondary"><strong>Waiting Indicator message</strong></div>
<div class="p-4 flex flex-row flex-wrap justify-around items-center">
    <mat-form-field class="flex-auto w-80 mr-5">
        <mat-label>Custom message</mat-label>
        <input matInput
            type="text"
            [formControl]="customMessage">

        <mat-hint align="end">Text value</mat-hint>
        <mat-error *ngIf="customMessage.hasError('required')">Required</mat-error>
    </mat-form-field>
    <button mat-raised-button
        class="flex-none w-32 mr-5 mb-3"
        color="primary"
        [disabled]="!customMessage.valid"
        (click)="onDisplayWaitingIndicator()">Show Indicator
    </button>
    <button mat-raised-button
        class="flex-none w-32 mr-5 mb-3"
        color="primary"
        (click)="onHideWaitingIndicator()">Hide Indicator
    </button>
</div>

<dike-grid id="grid-structure" #grid="dkgGrid">
</dike-grid>
```

{% endtab %}

{% tab title="grid-structure.component.ts" %}

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class GridStructureComponent implements OnInit, OnDestroy {

  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;

  customMessage: FormControl;

  constructor() {
    this.customMessage = new FormControl('', Validators.required);
  }

  onDisplayWaitingIndicator(): void {
    this.dikeGrid.waitingIndicator.customMessageIndicator(this.customMessage.value as string);
  }

  onHideWaitingIndicator(): void {
    this.dikeGrid.waitingIndicator.hideWaitingIndicator();
  }
}

```

{% endtab %}
{% endtabs %}

The previous configuration adds:

1. A textbox to enter the custom message.
2. Two buttons to display and hide the Waiting Indicator, respectively.
3. Callbacks in the prior UI controls.

Then, clicking on the **Show Indicator** button generates the following output:

![Displaying the indicator using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FjEbd2eZlqVkkeRXvXAss%2Fgrid-structure-waiting-indicator-custom-msg.png?alt=media\&token=1a2c2611-3f51-41d9-bc2f-615ffe29bbc6)


# DataSource

This section describes how you can provide a data set and how a DikeGrid instance treats that data set.

## Live examples

{% hint style="success" %}
[In-Memory DataSource](https://demos.dikesoft.com/dk-grid/datasource/in-memory) live example.
{% endhint %}

{% hint style="success" %}
[Custom DataSource](https://demos.dikesoft.com/dk-grid/datasource/custom) live example.
{% endhint %}

## Specifying data

Every DikeGrid instance accepts data from an <mark style="color:green;">`Array`</mark>, <mark style="color:green;">`Observable< Array>`</mark>, a <mark style="color:green;">`DikeGridDataSource`</mark>, or simply a <mark style="color:green;">`DataSource`</mark>.

{% hint style="info" %}
Remember, the <mark style="color:green;">`DataSource`</mark> definition is an <mark style="color:blue;">`abstract`</mark> class with two methods: `connect()` and `disconnect()`. Therefore, you can provide an instance of a class that derives from the <mark style="color:green;">`DataSource`</mark> class.
{% endhint %}

Depending on the data you provide, there are two types of DataSource: an **In-Memory** DataSource and a **Custom** DataSource.

## Wrapping data entries

It does not matter how you provide your data set. The DikeGrid will **wrap** every data entry in a <mark style="color:green;">`DikeGridDataRowEntry`</mark> instance.

The <mark style="color:green;">`DikeGridDataRowEntry`</mark> wrapper helps to know several features during execution, for instance, when the user modifies a data row or when it is selected, the row status, among others.

{% hint style="success" %}
For further details, see the [<mark style="color:green;">`DikeGridDataRowEntry`</mark>](/reference/classes/rows#dikegriddatarowentry-less-than-t-greater-than) definition.
{% endhint %}

Once the DikeGrid **receives** and **wraps** the data, it assigns and emits a **unique id**. You can get this id by listening to the `dataDeliveryIdChange` event.

{% hint style="info" %}
Indeed, a **DikeGrid** instance will send a **unique id** every time you provide a new set of entries. It means that you can have an **Observable** or a <mark style="color:green;">`DikeGridDataSource`</mark>, and if you change the entries set, the DikeGrid will wrap these new entries.
{% endhint %}

Let us listen to the `dataDeliveryIdChange` event:

{% tabs %}
{% tab title="in-memory-data-source.component.html" %}

```markup
<dike-grid id="grid-in-memory-datasource" height="650px" #grid="dkgGrid"
    (dataDeliveryIdChange)="onDataDeliveryIdChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="in-memory-data-source.component.ts" %}

```typescript
onDataDeliveryIdChange(id: string): void {
  console.log(`Data delivery id: ${id}`);
}
```

{% endtab %}
{% endtabs %}

Open your **dev console** to see the emitted id.

{% hint style="info" %}
The DikeGrid internally creates a <mark style="color:green;">`DikeGridDataSource`</mark> instance to wrap every data entry.
{% endhint %}

{% hint style="warning" %}
Be aware that every time a DikeGrid instance wraps the provided entries, it **resets** the **edition** and **selection** operations. It means that the DikeGrid removes the history of changes and deselects rows.
{% endhint %}

### Row Ids

The DikeGrid sets a **unique id** number for every **row** during the wrapping operation. This id number is a consecutive number starting at the **zero number**.

You can open any live example, go to the [Floating Configuration Panel](/#floating-configuration-panel), scroll to the **Rows** section and mark the **Row Id** checkbox.

![Floating Configuration Panel - Rows](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FvZFuFLhgPkpXsleW5J9q%2Fgrid-structure-type-content-row-height.png?alt=media\&token=861d95e7-df97-45d2-866e-8133179d441a)

![Row Ids](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FZuvnTVUV0aeSn0lNU7nT%2Fin-memory-datasource-row-id.png?alt=media\&token=efaaddcb-42c0-4c0f-b4cd-bbe5240a9e5f)

### Timestamp / Row status

Other values that the DikeGrid creates and assigns to every row are the **timestamp** and the initial **row status**.

The timestamp corresponds to the row **creation** time and the row status to the <mark style="color:red;">`Read`</mark> value. The <mark style="color:red;">`Read`</mark> value means that the user has not modified the row.

{% hint style="info" %}
The DikeGrid uses the row status and the timestamp properties to manage the edition operations. See the [Row Edition](/editing/row-edition) section.
{% endhint %}


# In-Memory DataSource

We name an In-Memory data source when you provide your data set in one go.

Providing a data set in one go means that the DikeGrid receives the data in any of the following ways:

| Data set                                               | Description                                                                                                                                                               |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:green;">`Array`</mark>              | When you directly assign a variety of entries.                                                                                                                            |
| <mark style="color:green;">`Observable<Array>`</mark>  | This type of data usually comes from an `HTTP get` invocation.                                                                                                            |
| <mark style="color:green;">`DikeGridDataSource`</mark> | You can create an instance of <mark style="color:green;">`DikeGridDataSource`</mark>, then assign your data to the <mark style="color:orange;">`entries`</mark> property. |

When you provide your data in **one go**, the DataGrid manages the data in **memory** during all the execution.

Since you can change the <mark style="color:orange;">`datasource`</mark> property at runtime, consider the following code snippet:

{% tabs %}
{% tab title="in-memory-data-source.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)="onArrayDataSource()">Array
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onDikeGridDataSource()">DikeGridDataSource
    </button>
    
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onObservableDataSource()">Observable
    </button>
</div>

<dike-grid id="grid-in-memory-datasource" height="650px" #grid="dkgGrid">
</dike-grid>

```

{% endtab %}

{% tab title="in-memory-data-source.component.ts" %}

```typescript
onArrayDataSource(): void {
  // Create an array of five entries:
  this.dkgDataSource = [{
    employeeId: '43c8a235-a95d-4e3e-bc30-f31ff33d490d',
    firstName: 'Dolores',
    lastName: 'Zieme',
    gender: 'female',
    age: 51,
    email: 'dolores.zieme@gmail.com',
    hireDate: new Date(2020, 7, 6),
    country: 'Mexico'
  }, {
    employeeId: '0f1bc4f3-f746-489a-9c24-eb570a16e556',
    firstName: 'Celia',
    lastName: 'Mertz',
    gender: 'female',
    age: 55,
    email: 'celia.mertz@gmail.com',
    hireDate: new Date(2010, 5, 11),
    country: 'Netherlands'
  }, {
    employeeId: '88acf950-dee5-480a-a67e-1b0419c9a473',
    firstName: 'Jeffery',
    lastName: 'Renner',
    gender: 'male',
    age: 60,
    email: 'jeffery.renner@hotmail.com',
    hireDate: new Date(2016, 11, 22),
    country: 'Bulgaria'
  }, {
    employeeId: '03a4e13f-98b4-4998-a40b-b66a832f327e',
    firstName: 'Annette',
    lastName: 'Satterfield',
    gender: 'female',
    age: 37,
    email: 'annette.satterfield@yahoo.com',
    hireDate: new Date(2019, 5, 2),
    country: 'Germany'
  }, {
    employeeId: 'a800045a-5127-4a5f-b93f-635f162ad376',
    firstName: 'Geraldine',
    lastName: 'Weimann',
    gender: 'female',
    age: 42,
    email: 'annette.satterfield@yahoo.com',
    hireDate: new Date(2016, 10, 19),
    country: 'United States of America'
  }] as Partial<Employee>[];
}

onDikeGridDataSource(): void {
  // Create a DikeGridDataSource object:
  const gridDataSource = new DikeGridDataSource<Employee>();
  // Take 2000 entries from the REST service:
  this.sampleData.getEmployees(2000).subscribe(employees => gridDataSource.entries = employees);
  // Assign the new DikeGridDataSource dataSource:
  this.dkgDataSource = gridDataSource;
}

onObservableDataSource(): void {
  // Take 500 entries from the REST service:
  this.dkgDataSource = this.sampleData.getEmployees(500);
}

```

{% endtab %}
{% endtabs %}

With the previous UI, you can do the following actions:

1. **Array** button. It creates an array of five entries.
2. **Observable** button. It makes a call to an `HTTP get`. Then, it retrieves 500 entries from the REST API.
3. **DikeGridDataSource** button. It creates a <mark style="color:green;">`DikeGridDataSource`</mark> instance. Then it makes a call to an `HTTP get`. We assign the response to the <mark style="color:orange;">`entries`</mark> property from the <mark style="color:green;">`DikeGridDataSource`</mark>.

You can see how the **unique id** changes every time the <mark style="color:orange;">`datasource`</mark> property changes.

## DataSource Decorators

Once you assign your data source to the DikeGrid instance, it takes those rows into a **pipe** of **operations**.

See the following diagram:

![In-Memory DataSource decorators](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4MoO9WAp5W3RAfuzPtt2%2Fin-memory-datasource-decorators.png?alt=media\&token=40803e77-f581-4565-966c-127f8f342757)

{% hint style="success" %}
The <mark style="color:green;">`DikeGridDataSource`</mark> instance is always the first instance in the chain.
{% endhint %}

As you can see, the DikeGrid creates the following instances:

| Decorator class                                                | Description                                                                                                                                                                                                                                                              |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <mark style="color:green;">`DikeRefreshingDataSource`</mark>   | This decorator always wraps a <mark style="color:green;">`DikeGridDataSource`</mark> instance. It pushes the rows when the user has **updated** them. The DikeGrid creates this decorator if you have allowed the edition operation.                                     |
| <mark style="color:green;">`DikeEditionStateDataSource`</mark> | It splits the set of rows depending on their status, <mark style="color:red;">`Modified`</mark>, <mark style="color:red;">`Deleted`</mark>, or <mark style="color:red;">`Editing`</mark>. The DikeGrid creates this decorator if you have allowed the edition operation. |
| <mark style="color:green;">`DikeFilteringDataSource`</mark>    | This decorator manages all the filtering operations. Even the filters applied to the rows in edition mode.                                                                                                                                                               |
| <mark style="color:green;">`DikeSortingDataSource`</mark>      | This decorator manages all the sorting operations.                                                                                                                                                                                                                       |

We based on the [Decorator](https://refactoring.guru/design-patterns/decorator) pattern to design the previous **chain** of **operations**. That is why we named every instance as a **decorator**. Every decorator wraps the last decorator instance.

This pattern gives us great **flexibility** in **adding** or **removing** operations to the chain and **encapsulating responsibilities** in each decorator.

{% hint style="info" %}
The DikeGrid will set the <mark style="color:green;">`DikePagingDataSource`</mark> decorator at the end of the chain of operations or after the created tree due to the row grouping operation.
{% endhint %}

### Custom Decorators

You can create your decorators and add them to the chain of operations. To create a decorator, you have to extend from the <mark style="color:blue;">`abstract`</mark> class [<mark style="color:green;">`DikeDecoratorDataSource`</mark>](/reference/classes/datasource#dikedecoratordatasource-less-than-t-greater-than).

Let us define a custom decorator:

{% code title="custom-datasource-decorator.ts" %}

```typescript
import { map, tap } from 'rxjs/operators';

import { DikeDataSource } from '@dikesoft/angular-data-grid';
import { DikeDecoratorDataSource } from '@dikesoft/angular-data-grid';
import { DikeGridDataRowEntry } from '@dikesoft/angular-data-grid';

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

export class CustomDecoratorDataSource extends DikeDecoratorDataSource<Employee> {

  constructor(
    dikeDataSource: DikeDataSource<DikeGridDataRowEntry<Employee>>) {

    super(dikeDataSource);
    this.setChangeSubscription();
  }

  protected setChangeSubscription(): void {
    // As a wrapper, get the previous result stream:
    const dataStream$ = this.dikeDataSource.connect().pipe(
      tap(rows => console.log('tap-CustomDecoratorDataSource: ', rows)),
      map((rows) => {
        rows.forEach((row) => {
          let finalEmployeeId = '';
          const employeeIdSplit = row.entry.employeeId.split('-');

          for (let i=0; i<employeeIdSplit.length - 1; i++) {
            finalEmployeeId += 'x'.repeat(employeeIdSplit[i].length) + '-';
          }

          row.entry.employeeId = finalEmployeeId + employeeIdSplit[employeeIdSplit.length - 1];
        });

        return rows;
      })
    );

    this.changeSubscription.unsubscribe();
    this.changeSubscription = dataStream$.subscribe(rows => this.data = rows);
  }
}

```

{% endcode %}

In the previous code, we only hide the **Employee Id** value. We intentionally left a tap RxJS operator to verify that the DikeGrid invokes the custom decorator.

Once you have defined your decorators, you can add them to the chain of operations, implementing the function [<mark style="color:green;">`DataSourceDecoratorFn`</mark>](/reference/type-aliases/datasource#datasourcedecoratorfn-less-than-t-greater-than). Then, you will assign it to the input property named <mark style="color:orange;">`decoratorFn`</mark>.

{% hint style="warning" %}
Be aware that you can assign the <mark style="color:orange;">`decoratorFn`</mark> property only at the initialization phase.
{% endhint %}

Let us assign the <mark style="color:orange;">`decoratorFn`</mark> property.

{% code title="in-memory-data-source.component.ts" %}

```typescript
ngOnInit(): void {
  //...
  
  // We create our chain of operations implementing the DataSourceDecoratorFn function:
  this.dkgDecoratorFn = (dataSource: DikeGridDataSource<Employee>, dikeGrid: DikeGridComponent<Employee>): DikeDataSource<DikeGridDataRowEntry<Employee>> => {
    // Firstly, point to the given DikeGridDataSource<Employee>, then add the decorators:
    let dikeDecoratorDataSource: DikeDataSource<DikeGridDataRowEntry<Employee>> = dataSource;

    // Custom decorator:
    dikeDecoratorDataSource = new CustomDecoratorDataSource(dikeDecoratorDataSource);
    // Filtering decorator:
    dikeDecoratorDataSource = dikeGrid.dataSourceFactory.createFilteringDecorator(dikeDecoratorDataSource);
    // Sorting decorator:
    dikeDecoratorDataSource = dikeGrid.dataSourceFactory.createSortingDecorator(dikeDecoratorDataSource);

    return dikeDecoratorDataSource;
  };
}
```

{% endcode %}

As you can see, you have access to the property named <mark style="color:orange;">`dataSourceFactory`</mark> under the DikeGrid instance.

{% hint style="success" %}
The <mark style="color:orange;">`dataSourceFactory`</mark> property is of type [<mark style="color:green;">`DikeGridFactoryDataSource`</mark>](/reference/dkgrid-api/dkgridfactorydatasource). It is a **read-only** property used to create **filtering** and **sorting** decorators.
{% endhint %}

## Decorators and Row Grouping operation

When the user groups rows, the DikeGrid internally creates a tree where every node represents a group of data rows. Only the leaves have the actual data rows.

According to the previous statement, the DikeGrid takes the defined chain of operations and sets it to every tree leaf.

## Summary

This type of **DataSource** receives the data in **one go**. Therefore, the DikeGrid manages the provided data in **memory** during all execution. Furthermore, DikeGrid uses the [Decorator](https://refactoring.guru/design-patterns/decorator) pattern to create a **chain** of **operations**. Thus the DikeGrid handles, for instance, **filtering** and **sorting** processes, among others.

### Complete code for this section

{% tabs %}
{% tab title="in-memory-data-source.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)="onArrayDataSource()">Array
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onDikeGridDataSource()">DikeGridDataSource
    </button>
    
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onObservableDataSource()">Observable
    </button>
</div>

<dike-grid id="grid-in-memory-datasource" height="650px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (dataDeliveryIdChange)="onDataDeliveryIdChange($event)"

    allowRowGrouping
    [allowColumnDragging]="gridProperties.allowColumnDragging"
    [allowRowFiltering]="gridProperties.allowRowFilter"
    [allowSorting]="gridProperties.allowSorting"
    [allowPagination]="gridProperties.allowPagination"
    [pageSize]="gridProperties.pageSize"

    allowEdition
    [editionMode]="gridProperties.editionMode"

    [decoratorFn]="dkgDecoratorFn"
    [datasource]="dkgDataSource">
    
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350"
        sortable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250"
        sortable>
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            editable>
        </dike-grid-column>

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

    </dike-grid-column>
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        sortable
        groupable
        draggable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="100"
        sortable
        editable
        groupable
        draggable>
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="email"
        headerText="Email"
        dataType="Text"
        width="300"
        sortable
        editable>
    </dike-grid-column>

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

</dike-grid>
  
```

{% endtab %}

{% tab title="in-memory-data-source.component.ts" %}

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

import { DataSourceDecoratorFn, DikeDataSource, DikeGridComponent, DikeGridDataRowEntry,
    DikeGridDataSource, DikeGridDataSourceInput } 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 { CustomDecoratorDataSource } from './custom-datasource-decorator';

import { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

@Component({
  selector: 'in-memory-data-source',
  templateUrl: './in-memory-data-source.component.html',
  styleUrls: ['./in-memory-data-source.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class InMemoryDataSourceComponent implements OnInit, OnDestroy {

  dkgDecoratorFn: DataSourceDecoratorFn<Employee>;
  dkgDataSource: DikeGridDataSourceInput<Partial<Employee>>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  ngOnInit(): void {
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
    // Assign 1000 entries from the REST API:
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // We create our chain of operations implementing the DataSourceDecoratorFn function:
    this.dkgDecoratorFn = (dataSource: DikeGridDataSource<Employee>, dikeGrid: DikeGridComponent<Employee>): DikeDataSource<DikeGridDataRowEntry<Employee>> => {
      // Firstly, point to the given DikeGridDataSource<Employee>, then add the decorators:
      let dikeDecoratorDataSource: DikeDataSource<DikeGridDataRowEntry<Employee>> = dataSource;

      // Custom decorator:
      dikeDecoratorDataSource = new CustomDecoratorDataSource(dikeDecoratorDataSource);
      // Filtering decorator:
      dikeDecoratorDataSource = dikeGrid.dataSourceFactory.createFilteringDecorator(dikeDecoratorDataSource);
      // Sorting decorator:
      dikeDecoratorDataSource = dikeGrid.dataSourceFactory.createSortingDecorator(dikeDecoratorDataSource);

      return dikeDecoratorDataSource;
    };
  }

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

  onDataDeliveryIdChange(id: string): void {
    console.log(`Data delivery id: ${id}`);
  }

  onArrayDataSource(): void {
    // Create an array of five entries:
    this.dkgDataSource = [{
        employeeId: '43c8a235-a95d-4e3e-bc30-f31ff33d490d',
        firstName: 'Dolores',
        lastName: 'Zieme',
        gender: 'female',
        age: 51,
        email: 'dolores.zieme@gmail.com',
        hireDate: new Date(2020, 7, 6),
        country: 'Mexico'
    }, {
        employeeId: '0f1bc4f3-f746-489a-9c24-eb570a16e556',
        firstName: 'Celia',
        lastName: 'Mertz',
        gender: 'female',
        age: 55,
        email: 'celia.mertz@gmail.com',
        hireDate: new Date(2010, 5, 11),
        country: 'Netherlands'
    }, {
        employeeId: '88acf950-dee5-480a-a67e-1b0419c9a473',
        firstName: 'Jeffery',
        lastName: 'Renner',
        gender: 'male',
        age: 60,
        email: 'jeffery.renner@hotmail.com',
        hireDate: new Date(2016, 11, 22),
        country: 'Bulgaria'
    }, {
        employeeId: '03a4e13f-98b4-4998-a40b-b66a832f327e',
        firstName: 'Annette',
        lastName: 'Satterfield',
        gender: 'female',
        age: 37,
        email: 'annette.satterfield@yahoo.com',
        hireDate: new Date(2019, 5, 2),
        country: 'Germany'
    }, {
        employeeId: 'a800045a-5127-4a5f-b93f-635f162ad376',
        firstName: 'Geraldine',
        lastName: 'Weimann',
        gender: 'female',
        age: 42,
        email: 'annette.satterfield@yahoo.com',
        hireDate: new Date(2016, 10, 19),
        country: 'United States of America'
    }] as Partial<Employee>[];
  }

  onDikeGridDataSource(): void {
    // Create a DikeGridDataSource object:
    const gridDataSource = new DikeGridDataSource<Employee>();
    // Take 2000 entries from the REST API:
    this.sampleData.getEmployees(2000).subscribe(employees => gridDataSource.entries = employees);
    // Assign the new DikeGridDataSource dataSource:
    this.dkgDataSource = gridDataSource;
  }

  onObservableDataSource(): void {
    // Take 500 entries from the REST API:
    this.dkgDataSource = this.sampleData.getEmployees(500);
  }

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

```

{% endtab %}

{% tab title="custom-datasource-decorator.ts" %}

```typescript
import { map, tap } from 'rxjs/operators';
import { DikeDataSource, DikeDecoratorDataSource, 
  DikeGridDataRowEntry } from '@dikesoft/angular-data-grid';
import { Employee } from 'app/mock-api/common/employees/data.model';

export class CustomDecoratorDataSource extends DikeDecoratorDataSource<Employee> {

  constructor(
    dikeDataSource: DikeDataSource<DikeGridDataRowEntry<Employee>>) {

    super(dikeDataSource);
    this.setChangeSubscription();
  }

  protected setChangeSubscription(): void {
    // As a wrapper, get the previous result stream:
    const dataStream$ = this.dikeDataSource.connect().pipe(
      tap(rows => console.log('tap-CustomDecoratorDataSource: ', rows)),
      map((rows) => {
        rows.forEach((row) => {
          let finalEmployeeId = '';
          const employeeIdSplit = row.entry.employeeId.split('-');

          for (let i=0; i<employeeIdSplit.length - 1; i++) {
            finalEmployeeId += 'x'.repeat(employeeIdSplit[i].length) + '-';
          }

          row.entry.employeeId = finalEmployeeId + employeeIdSplit[employeeIdSplit.length - 1];
        });

        return rows;
      })
    );

    this.changeSubscription.unsubscribe();
    this.changeSubscription = dataStream$.subscribe(rows => this.data = rows);
  }
}

```

{% endtab %}
{% endtabs %}


# Custom DataSource

A custom data source is an instance class that extends from the DataSource abstract class.

We name Custom DataSource when you provide a data source that extends from the <mark style="color:blue;">`abstract`</mark> class <mark style="color:green;">`DataSource`</mark>.

When the DikeGrid receives this type of data source, it connects to this DataSource and creates an instance of the type <mark style="color:green;">`DikeGridDataSource`</mark>, wrapping every data entry into a <mark style="color:green;">`DikeGridDataRowEntry`</mark> row.

Same as In-Memory DataSource, after wrapping operation, the DikeGrid **assigns** and **emits** a **unique id**. It also sets a **unique id** for every **row**.

See the following code snippet:

{% tabs %}
{% tab title="custom-data-source.component.html" %}

```markup
<dike-grid id="grid-custom-datasource" height="600px" #grid="dkgGrid"
    (dataDeliveryIdChange)="onDataDeliveryIdChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="custom-data-source.component.ts" %}

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CustomDataSourceComponent implements OnInit, AfterViewInit, OnDestroy {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;
 
  ngAfterViewInit(): void {
    // Once the DikeGrid intance is retrieved from the view, we create the DataSource instance:
    Promise.resolve().then(() => {
      this.dkgDataSource = new CustomDataSource(this.sampleData, this.dikeGrid.filter,
        this.dikeGrid.sorting, this.dikeGrid.pagination, this.dikeGrid.waitingIndicator);
    });
  }
  
  onDataDeliveryIdChange(id: string): void {
    console.log(`Data delivery id: ${id}`);
  } 
}

```

{% endtab %}

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

```typescript
import { DataSource } from '@angular/cdk/collections';
import { PageEvent } from '@angular/material/paginator';

import { combineLatest, defer, merge, Observable, Subject, Subscription } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';

import { DikeColumnSortEvent, DikeFilterable, DikeGridFilter, DikeGridPagination,
    DikeGridSorting, DikeGridWaitingIndicator
} from '@dikesoft/angular-data-grid';

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

export class CustomDataSource extends DataSource<Employee> {

  private dataSetSubject: Subject<Employee[]>;
  private gridSubscription: Subscription = Subscription.EMPTY;

  constructor(
    private sampleData: SampleData,
    private gridFilter: DikeGridFilter<Employee>,
    private gridSorting: DikeGridSorting<Employee>,
    private gridPagination: DikeGridPagination,
    private gridIndicator: DikeGridWaitingIndicator) {

    super();

    this.dataSetSubject = new Subject<Employee[]>();
    this.setGridEventSubscription();
  }

  connect(): Observable<Employee[]> {
    return this.dataSetSubject.asObservable();
  }

  disconnect(): void {
    this.gridSubscription.unsubscribe();
  }

  private initialized(): Observable<void> {
    return defer(() => new Observable<void>((observer) => {
      observer.next();
      observer.complete();
    }));
  }

  private page(entries: Employee[]): Employee[] {
    // If paginator is not displayed, return the original entries:
    if (!this.gridPagination.matPaginator) {
      return entries;
    }

    const startIndex = this.gridPagination.matPaginator.pageIndex * this.gridPagination.matPaginator.pageSize;

    return entries.slice(startIndex, startIndex + this.gridPagination.matPaginator.pageSize);
  }

  private setGridEventSubscription(): void {
    // Prepare the filter observable:
    const gridFiltering$: Observable<DikeFilterable<Employee>[]> = merge(
      this.gridFilter.filterChange,
      this.gridFilter.clearFilterChange,
      this.initialized()

    ).pipe(
      map(() => {
        const validFilterables: DikeFilterable<Employee>[] = [ ];

        // Get all the valid filterables:
        this.gridFilter.filterables.forEach((filterable: DikeFilterable<Employee>) => {
          if (!!filterable.filter && filterable.filter.length > 0) {
            validFilterables.push({ ...filterable });
          }
        });

        return validFilterables;
      })
    );

    // Prepare the sorting observable:
    const gridSorting$: Observable<DikeColumnSortEvent<Employee>> = merge(this.gridSorting.sortChange, this.initialized()).pipe(
      map(() => this.gridSorting.getCurrentSortable())
    );

    /**
     * Prepare the pagination event:
     *
     * Since the paginator could be hidden at runtime, we listen the paginatorChange
     * observable.
     *
     * If the paginator is displayed, we listen to the pageChange observable. Otherwise,
     * we return a void observable.
     *
     */
    const gridPaging$: Observable<PageEvent | void> = merge(this.gridPagination.paginatorChange, this.initialized())
      .pipe(
        switchMap(paginator => !!paginator ? merge(paginator.page, this.initialized()) : this.initialized()
      )
    );

    // Execute the actual REST invocation:
    const gridServerData$: Observable<Employee[]> = combineLatest([gridFiltering$, gridSorting$]).pipe(
      tap(() => this.gridIndicator.loadingIndicator()),
      // Cancel the previous invocation and make a new one:
      switchMap(([filterables, sorting]) => this.sampleData.searchEmployees(filterables, sorting)),
    );

    // For the sake of brevity, pagination is done in the client side:
    const pagedData$ = combineLatest([gridServerData$, gridPaging$]).pipe(
      tap(([entries]) => {
        if (!!this.gridPagination.matPaginator && this.gridPagination.matPaginator.length !== entries.length) {
          this.gridPagination.matPaginator.length = entries.length;
          this.gridPagination.matPaginator.firstPage();
        }
      }),
      map(([entries]) => this.page(entries))
    );

    this.gridSubscription.unsubscribe();
    this.gridSubscription = pagedData$.pipe(
      // Once the data set arrives, we hide the loading indicator:
      tap(() => this.gridIndicator.hideWaitingIndicator())

    ).subscribe(entries => this.dataSetSubject.next(entries));
  }
}

```

{% endtab %}

{% tab title="sample-data.service.ts" %}

```typescript
import { HttpClient } from '@angular/common/http';

import { Observable } from 'rxjs';
import { delay } from 'rxjs/operators';

import { DikeFilterable, DikeColumnSortEvent } from '@dikesoft/angular-data-grid';
import { Employee } from 'app/mock-api/common/employees/data.model';

@Injectable({
  providedIn: 'root'
})
export class SampleData {

  constructor(
    private httpClient: HttpClient) { }

  getEmployees(sampleSize: number): Observable<Employee[]> {
    return this.httpClient.get<Employee[]>('api/common/employees', { params: { sampleSize } }).pipe(delay(350));
  }

  searchEmployees(filterables: DikeFilterable<Employee>[], sortedColumn: DikeColumnSortEvent<Employee>): Observable<Employee[]> {
    return this.httpClient.post<Employee[]>('api/common/employees', { filterables, sortedColumn }).pipe(delay(350));
  }
}

```

{% endtab %}
{% endtabs %}

1. After querying the DikeGrid instance from the component's view, we create a <mark style="color:green;">`CustomDataSource`</mark> object passing in all the dependencies to implement the logic for this data source.
2. We obtain the data from a **REST API**. This API is a mock service that runs in memory. That is why we delay every invocation **350** milliseconds.
3. The **REST API** receives the **filtering** and **sorting** event data to process them behind the REST API.
4. For the sake of brevity, we execute the pagination operation in the client after receiving the response from the **REST API**.

{% hint style="warning" %}
Be aware that the DikeGrid will **wrap** every response from the REST API assigning **new ids** for every row and a **unique id** for the whole data set. The DikeGrid also **resets** the **edition** and **selection** operations. It means that the DikeGrid removes the history of changes and deselects rows.
{% endhint %}

## DataSource decorators

Once you assign your custom data source to the DikeGrid instance, it connects to that data source.

See the following diagram:

![Custom DataSource decorators](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FukTrbxYAFx9NBKXHx4qi%2Fcustom-datasource-decorators.png?alt=media\&token=25f44bdd-ffb6-4a69-bdca-bc772e2e3cae)

{% hint style="success" %}
The <mark style="color:green;">`DikeGridDataSource`</mark> wraps every data entry into a row of the type <mark style="color:green;">`DikeGridDataRowEntry`</mark>.
{% endhint %}

If you have allowed the **edition** operation, the DikeGrid creates the following decorators:

|                                                                    |                                                                                                                                                                                           |
| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:green;">`DikeRefreshingDataSource`</mark>       | This decorator always wraps a <mark style="color:green;">`DikeGridDataSource`</mark> instance. It pushes the rows when the user has **updated** them.                                     |
| <mark style="color:green;">`DikeEditionStateDataSource`</mark>     | It splits the set of rows depending on their status, <mark style="color:red;">`Modified`</mark>, <mark style="color:red;">`Deleted`</mark>, or <mark style="color:red;">`Editing`</mark>. |
| <mark style="color:green;">`DikeEditionFilteringDataSource`</mark> | This decorator manages all the filtering operations over the edited rows.                                                                                                                 |

{% hint style="info" %}
The DikeGrid creates the edition decorators only if you allow edition because the DikeGrid manages all the edition operations in memory.
{% endhint %}

{% hint style="warning" %}
You can not assign **custom decorators** when you provide a **Custom DataSource**.
{% endhint %}

{% hint style="warning" %}
**Row grouping** operation is **not allowed** when you provide a **Custom DataSource**.
{% endhint %}

## Summary

You can provide a custom data source to the DikeGrid. For example, a custom data source could take remote data entries.&#x20;

The DikeGrid manages the edition operation by adding the corresponding decorators.

Be aware that you can not group rows or assign custom decorators.

### Complete code for this section

{% tabs %}
{% tab title="custom-data-source.component.html" %}

```markup
<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)="onClearFilter()">Clear Filter
    </button>
</div>

<dike-grid id="grid-custom-datasource" height="600px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"
    
    (dataDeliveryIdChange)="onDataDeliveryIdChange($event)"
    
    [allowSelection]="gridProperties.allowSelection"
    [allowRowFiltering]="gridProperties.allowRowFilter"
    [allowSorting]="gridProperties.allowSorting"
    [allowPagination]="gridProperties.allowPagination"
    [pageSize]="gridProperties.pageSize"
    
    (filterChange)="onFilterChange($event)"
    (editionFilterChange)="onEditionFilterChange($event)"
    
    (selectionChange)="onSelectionChange($event)"
    (editionRowChange)="onEditionRowChange($event)"
    (updateRowChange)="onUpdateRowChange($event)"
    (cancelRowEditionChange)="onCancelRowEditionChange($event)"
    (removeRowChange)="onRemoveRowChange($event)"
    (restoreRowChange)="onRestoreRowChange($event)"
    
    allowEdition
    [editionMode]="gridProperties.editionMode"
    
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250"
        sortable>
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            editable>
        </dike-grid-column>

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

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

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

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

</dike-grid>
    
```

{% endtab %}

{% tab title="custom-data-source.component.ts" %}

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

import { DikeFilterable, DikeGridComponent, DikeGridDataRowEntry, DikeGridDataSourceInput } 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 { CustomDataSource } from 'app/modules/admin/datasource/custom-data-source/custom-datasource';

import { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

@Component({
  selector: 'custom-data-source',
  templateUrl: './custom-data-source.component.html',
  styleUrls: ['./custom-data-source.component.scss'],

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  ngOnInit(): void {
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

  ngAfterViewInit(): void {
    // Once the DikeGrid intance is retrieved from the view, we create the DataSource instance:
    Promise.resolve().then(() => {
      this.dkgDataSource = new CustomDataSource(this.sampleData, this.dikeGrid.filter,
        this.dikeGrid.sorting, this.dikeGrid.pagination, this.dikeGrid.waitingIndicator);
    });
  }

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

  onDataDeliveryIdChange(id: string): void {
    console.log(`Data delivery id: ${id}`);
  }

  onFilterChange(filterable: DikeFilterable<Employee>): void {
    console.log('onFilterChange => ', filterable);
  }

  onEditionFilterChange(filterable: DikeFilterable<Employee>): void {
    console.log('onEditionFilterChange => ', filterable);
  }

  onSelectionChange(rows: DikeGridDataRowEntry<Employee>[]): void {
    console.log('Selection change: ', rows);
  }

  onEditionRowChange(row: DikeGridDataRowEntry<Employee>): void {
    console.log('Edition row change: ', row);
  }

  onUpdateRowChange(rows: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('Update row(s) change: ', rows);
  }

  onCancelRowEditionChange(rows: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('Cancel row(s) edition change: ', rows);
  }

  onRemoveRowChange(rows: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('Remove row(s) change: ', rows);
  }

  onRestoreRowChange(rows: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('Restore row(s) change: ', rows);
  }

  onClearFilter(): void {
    console.log('Clear Filter => ', this.dikeGrid.filter.clearFilter());
  }

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

```

{% endtab %}

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

```typescript
import { DataSource } from '@angular/cdk/collections';
import { PageEvent } from '@angular/material/paginator';

import { combineLatest, defer, merge, Observable, Subject, Subscription } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';

import { DikeColumnSortEvent, DikeFilterable, DikeGridFilter, DikeGridPagination,
    DikeGridSorting, DikeGridWaitingIndicator
} from '@dikesoft/angular-data-grid';

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

export class CustomDataSource extends DataSource<Employee> {

  private dataSetSubject: Subject<Employee[]>;
  private gridSubscription: Subscription = Subscription.EMPTY;

  constructor(
    private sampleData: SampleData,
    private gridFilter: DikeGridFilter<Employee>,
    private gridSorting: DikeGridSorting<Employee>,
    private gridPagination: DikeGridPagination,
    private gridIndicator: DikeGridWaitingIndicator) {

    super();

    this.dataSetSubject = new Subject<Employee[]>();
    this.setGridEventSubscription();
  }

  connect(): Observable<Employee[]> {
    return this.dataSetSubject.asObservable();
  }

  disconnect(): void {
    this.gridSubscription.unsubscribe();
  }

  private initialized(): Observable<void> {
    return defer(() => new Observable<void>((observer) => {
      observer.next();
      observer.complete();
    }));
  }

  private page(entries: Employee[]): Employee[] {
    // If paginator is not displayed, return the original entries:
    if (!this.gridPagination.matPaginator) {
      return entries;
    }

    const startIndex = this.gridPagination.matPaginator.pageIndex * this.gridPagination.matPaginator.pageSize;

    return entries.slice(startIndex, startIndex + this.gridPagination.matPaginator.pageSize);
  }

  private setGridEventSubscription(): void {
    // Prepare the filter observable:
    const gridFiltering$: Observable<DikeFilterable<Employee>[]> = merge(
      this.gridFilter.filterChange,
      this.gridFilter.clearFilterChange,
      this.initialized()

    ).pipe(
      map(() => {
        const validFilterables: DikeFilterable<Employee>[] = [ ];

        // Get all the valid filterables:
        this.gridFilter.filterables.forEach((filterable: DikeFilterable<Employee>) => {
          if (!!filterable.filter && filterable.filter.length > 0) {
            validFilterables.push({ ...filterable });
          }
        });

        return validFilterables;
      })
    );

    // Prepare the sorting observable:
    const gridSorting$: Observable<DikeColumnSortEvent<Employee>> = merge(this.gridSorting.sortChange, this.initialized()).pipe(
      map(() => this.gridSorting.getCurrentSortable())
    );

    /**
     * Prepare the pagination event:
     *
     * Since the paginator could be hidden at runtime, we listen the paginatorChange
     * observable.
     *
     * If the paginator is displayed, we listen to the pageChange observable. Otherwise,
     * we return a void observable.
     *
     */
    const gridPaging$: Observable<PageEvent | void> = merge(this.gridPagination.paginatorChange, this.initialized())
      .pipe(
        switchMap(paginator => !!paginator ? merge(paginator.page, this.initialized()) : this.initialized()
      )
    );

    // Execute the actual REST invocation:
    const gridServerData$: Observable<Employee[]> = combineLatest([gridFiltering$, gridSorting$]).pipe(
      tap(() => this.gridIndicator.loadingIndicator()),
      // Cancel the previous invocation and make a new one:
      switchMap(([filterables, sorting]) => this.sampleData.searchEmployees(filterables, sorting)),
    );

    // For the sake of brevity, pagination is done in the client side:
    const pagedData$ = combineLatest([gridServerData$, gridPaging$]).pipe(
      tap(([entries]) => {
        if (!!this.gridPagination.matPaginator && this.gridPagination.matPaginator.length !== entries.length) {
          this.gridPagination.matPaginator.length = entries.length;
          this.gridPagination.matPaginator.firstPage();
        }
      }),
      map(([entries]) => this.page(entries))
    );

    this.gridSubscription.unsubscribe();
    this.gridSubscription = pagedData$.pipe(
      // Once the data set arrives, we hide the loading indicator:
      tap(() => this.gridIndicator.hideWaitingIndicator())

    ).subscribe(entries => this.dataSetSubject.next(entries));
  }
}

```

{% endtab %}

{% tab title="sample-data.service.ts" %}

```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Observable } from 'rxjs';
import { delay } from 'rxjs/operators';

import { DikeFilterable, DikeColumnSortEvent } from '@dikesoft/angular-data-grid';
import { Employee } from 'app/mock-api/common/employees/data.model';

@Injectable({
  providedIn: 'root'
})
export class SampleData {

  constructor(
    private httpClient: HttpClient) { }

  getEmployees(sampleSize: number): Observable<Employee[]> {
    return this.httpClient.get<Employee[]>('api/common/employees', { params: { sampleSize } }).pipe(delay(350));
  }

  searchEmployees(filterables: DikeFilterable<Employee>[], sortedColumn: DikeColumnSortEvent<Employee>): Observable<Employee[]> {
    return this.httpClient.post<Employee[]>('api/common/employees', { filterables, sortedColumn }).pipe(delay(350));
  }
}

```

{% endtab %}
{% endtabs %}


# Theming

This section describes how to use your themes or use a pre-built one.

## Live example

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

## DikeGrid theming

The DikeGrid bases its theming process on the Angular Material's theming system. Therefore, DikeGrid exposes `color`, `typography`, and `theme` mixins.

{% hint style="success" %}
See the official docs for further details on [Angular Material's theming system](https://material.angular.io/guide/theming-your-components).
{% endhint %}

You have to define your theme and include DikeGrid's mixins and Angular Material's mixins in your application.

## Defining a theme

You have to create your primary and accent palettes with an optional warm palette when defining a theme. Once you have the palettes, you can create a **light** or a **dark** theme.

{% hint style="success" %}
See the official docs on [Theming Angular Material](https://material.angular.io/guide/theming).
{% endhint %}

Let us create a theme:

```scss
@use "@angular/material" as mat;
@use "@dikesoft/angular-data-grid" as dkg;

// The core mixin must be included exactly once for your application:
@include mat.core();

// The "warn" palette is optional:
$primary: mat.define-palette(mat.$indigo-palette);
$accent:  mat.define-palette(mat.$pink-palette, A200, A100, A400);

// You can define a dark theme as well:
$theme: mat.define-light-theme((
  color: (
    primary: $primary,
    accent: $accent
  )
));

// Emit styles for all Angular components:
@include mat.all-component-themes($theme);
// Emit style for the DikeGrid component:
@include dkg.theme($theme);
```

### Customizing typography

You can also provide your typography specifications when defining a theme.

```scss
@use "@angular/material" as mat;
@use "@dikesoft/angular-data-grid" as dkg;

// The core mixin must be included exactly once for your application:
@include mat.core();

// Define a custom typography:
$custom-typography: mat.define-typography-config(
  $font-family: Open Sans,
  $title: mat.define-typography-level(1.25rem, 2rem, 600),
  $body-2: mat.define-typography-level(0.875rem, 1.5rem, 600),
  $button: mat.define-typography-level(0.875rem, 0.875rem, 500),
  $input: mat.define-typography-level(0.875rem, 1, 400)
);

// The "warn" palette is optional:
$primary: mat.define-palette(mat.$indigo-palette);
$accent:  mat.define-palette(mat.$pink-palette, A200, A100, A400);

// You can define a dark theme as well:
$theme: mat.define-light-theme((
  color: (
    primary: $primary,
    accent: $accent
  ),
  typography: $custom-typography
));

// Emit styles for all Angular components:
@include mat.all-component-themes($theme);
// Emit style for the DikeGrid component:
@include dkg.theme($theme);
```

{% hint style="success" %}
See the official doc for [customizing typography](https://material.angular.io/guide/typography).
{% endhint %}

## Using a pre-built theme

DikeGrid includes four pre-built themes CSS files. Each of them uses the same palettes as Angular Material pre-built themes.

<table><thead><tr><th width="338.11648644704013">Theme</th><th width="150">Scheme</th><th>Palettes</th></tr></thead><tbody><tr><td><code>deeppurple-amber.css</code></td><td>Light</td><td>deep-purple, amber, red</td></tr><tr><td><code>indigo-pink.css</code></td><td>Light</td><td>indigo, pink, red</td></tr><tr><td><code>pink-bluegrey.css</code></td><td>Dark</td><td>pink, bluegrey, red</td></tr><tr><td><code>purple-green.css</code></td><td>Dark</td><td>purple, green, red</td></tr></tbody></table>

You can add the DikeGrid styles in two ways: **importing the CSS file** directly in the `styles.scss` file or adding the entry in the **styles array** from the `angular.json` file.

### Importing the CSS file

Open the `styles.scss` file and add the following line:

```scss
@import "@dikesoft/angular-data-grid/prebuilt-themes/indigo-pink.css";
```

### Adding the CSS file to the styles array

{% code title="angular.json" %}

```json
"styles": [
   "node_modules/@dikesoft/angular-data-grid/prebuilt-themes/indigo-pink.css",
   "src/styles.scss"
]
```

{% endcode %}

## Custom scrollbars

As you notice, all the live examples show **thin** and **colored** scrollbars.

By default, the DikeGrid applies styles to its scrollbars.

![Custom scrollbars](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FgJfQqjCYtyGkIUfFUEdF%2Ftheming-custom-scrollbars.png?alt=media\&token=0a15dd7f-d682-4332-8d1c-938a7b79badc)

You can unset this scrollbar customization by providing a <mark style="color:red;">`false`</mark> value through an Injection Token called <mark style="color:blue;">`CUSTOM_SCROLLBARS`</mark>.

Let us provide the <mark style="color:blue;">`CUSTOM_SCROLLBARS`</mark> Injection Token:

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

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

import { DikeDataGridModule, CUSTOM_SCROLLBARS } from '@dikesoft/angular-data-grid';
import { SharedModule } from 'app/shared/shared.module';

import { themingRoutes } from 'app/modules/admin/theming/theming.routing';
import { GridThemingComponent } from './grid-theming/grid-theming.component';

@NgModule({
  declarations: [
    GridThemingComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild(themingRoutes),

    SharedModule,
    DikeDataGridModule
  ],
  providers: [
    { provide: CUSTOM_SCROLLBARS, useValue: false }
  ]
})
export class ThemingModule { }

```

{% endcode %}

When providing the mentioned Injection Token, the DikeGrid shows the default scrollbars.

![Default scrollbars](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FAhzUAGbaqxbKV2lRgo37%2Ftheming-no-custom-scrollbars.png?alt=media\&token=b506e554-0d6a-4af4-9add-f3b3b442c79a)

## Summary

Since the DikeGrid bases its theming process on the Angular Material's theming system is very easy to customize your DikeGrid instances because you are familiar with that process.

{% tabs %}
{% tab title="grid-theming.component.html" %}

```markup
<dike-grid id="grid-theming" height="600px"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"
    
    [allowColumnDragging]="gridProperties.allowColumnDragging"

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

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

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

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

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

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

```

{% endtab %}

{% tab title="grid-theming.component.ts" %}

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

import { DikeGridDataSourceInput } 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: 'grid-theming',
  templateUrl: './grid-theming.component.html',
  styleUrls: ['./grid-theming.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class GridThemingComponent implements OnInit, OnDestroy {

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  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();
  }

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

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

```

{% endtab %}

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

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

import { DikeDataGridModule, CUSTOM_SCROLLBARS } from '@dikesoft/angular-data-grid';
import { SharedModule } from 'app/shared/shared.module';

import { themingRoutes } from 'app/modules/admin/theming/theming.routing';
import { GridThemingComponent } from './grid-theming/grid-theming.component';

@NgModule({
  declarations: [
    GridThemingComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild(themingRoutes),

    SharedModule,
    DikeDataGridModule
  ],
  providers: [
    { provide: CUSTOM_SCROLLBARS, useValue: false }
  ]
})
export class ThemingModule { }

```

{% endtab %}
{% endtabs %}


# Column Definitions

This section describes how to create columns in the component's HTML definition or in the TypeScript class file.

## Live example

{% hint style="success" %}
[Column Definitions](https://demos.dikesoft.com/dk-grid/column/definitions) live example.
{% endhint %}

## HTML definition

A column could be created in the HTML definition directly. Use the <mark style="color:red;">`dike-grid-column`</mark> selector just beneath the <mark style="color:red;">`dike-grid`</mark> selector.

In the following example three columns were defined using the [<mark style="color:green;">`DikeGridColumnComponent`</mark>](/reference/components/dkgridcolumncomponent#component-metadata) selector:

{% code title="column-definitions.component.html" %}

```markup
<dike-grid #grid="dkgGrid" id="grid-col-def" height="500px"
    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350">
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150">
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150">
    </dike-grid-column>

</dike-grid>
```

{% endcode %}

As it was mentioned in the [Quick Start Tutorial](/getting-started/quick-start-tutorial#column-definitions), the minimum number of properties to define a column are `fieldName`, `headerText` and `dataType`.

{% hint style="info" %}
For a complete list of column properties, see the [<mark style="color:green;">`DikeGridColumnComponent`</mark>](/reference/components/dkgridcolumncomponent#properties) definition.
{% endhint %}

## TypeScript code definition

It is also possible to define a column in the TypeScript's component file.

### DikeGrid Column API

In order to define a column in the TypeScript's component file, you must grab the corresponding **column API** of type <mark style="color:green;">`DikeGridColumnDef`</mark> from the <mark style="color:green;">`DikeGridComponent`</mark> instance you are defining columns.

There are two ways of grabbing this instance:

#### Querying the <mark style="color:green;">`DikeGridComponent`</mark> from the View

Every <mark style="color:green;">`DikeGridComponent`</mark> instance has its own <mark style="color:green;">`DikeGridColumnDef`</mark> instance. The instance is exposed as a public property called <mark style="color:orange;">`columnDef`</mark>.

{% hint style="info" %}
See the [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent) definition for a complete list of properties.
{% endhint %}

The following code snippet shows how the <mark style="color:green;">`DikeGridComponent`</mark> is retrieved and then, how **Gender** and **Age** columns are added using the column API.

{% tabs %}
{% tab title="column-definitions.component.html" %}

```markup
<dike-grid #grid="dkgGrid" id="grid-col-def" height="500px"
    gridElevation
    striped
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        sortable>
    </dike-grid-column>

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

</dike-grid>
```

{% endtab %}

{% tab title="column-definitions.component.ts" %}

```typescript
import { AfterViewInit, Component, OnInit, ViewChild, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core';

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

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

@Component({
  selector: 'column-definitions',
  templateUrl: './column-definitions.component.html',
  styleUrls: ['./column-definitions.component.scss'],
  
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnDefinitionsComponent implements OnInit, AfterViewInit {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;

  constructor(
    private sampleData: SampleData) { }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
  }

  ngAfterViewInit(): void {
    // The DikeGridComponent<T> instance is available:
    Promise.resolve().then(() => {
      // Define the Gender column:
      const genderColumn = new DikeBinaryColumnDef<Employee>('gender', 'Gender');
      genderColumn.width = 110;

      // Define the Age column:
      const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
      ageColumn.width = 85;

      // Finally, add them to the DikeGridComponent<T> instance:
      this.dikeGrid.columnDef.addColumns([ genderColumn, ageColumn ]);
    });
  }
}

```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
**Mixed Column Definition**. Notice how three columns are defined in the HTML and the others are defined in code.
{% endhint %}

#### Retrieving the <mark style="color:green;">`DikeGridColumnDef`</mark> instance

The <mark style="color:green;">`DikeGridColumnDef`</mark> instance could be retrieved listening to the event called <mark style="color:orange;">`gridColumnDefInstance`</mark> from the <mark style="color:green;">`DikeGridComponent`</mark>.

{% tabs %}
{% tab title="column-definitions.component.html" %}

```markup
<dike-grid #grid="dkgGrid" id="grid-col-def" height="500px"
    gridElevation
    striped
    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        sortable>
    </dike-grid-column>

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

</dike-grid>
```

{% endtab %}

{% tab title="column-definitions.component.ts" %}

```typescript
import { AfterViewInit, Component, OnInit, ViewChild, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core';

import { DikeGridComponent, DikeGridDataSourceInput, DikeBinaryColumnDef,
    DikeNumericColumnDef, DikeGridColumnDef, DikeTextColumnDef, DikeDateColumnDef
} from '@dikesoft/angular-data-grid';

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

@Component({
  selector: 'column-definitions',
  templateUrl: './column-definitions.component.html',
  styleUrls: ['./column-definitions.component.scss'],
  
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnDefinitionsComponent implements OnInit, AfterViewInit {

  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;

  constructor(
    private sampleData: SampleData) { }

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 300;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns([ emailColumn, hireDateColumn ]);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
  }

  ngAfterViewInit(): void {
    // The DikeGridComponent<T> instance is available:
    Promise.resolve().then(() => {

      // Define the Gender column:
      const genderColumn = new DikeBinaryColumnDef<Employee>('gender', 'Gender');
      genderColumn.width = 110;

      // Define the Age column:
      const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
      ageColumn.width = 85;

      // Finally, add them to the DikeGridComponent<T> instance:
      this.dikeGrid.columnDef.addColumns([ genderColumn, ageColumn ]);
    });
  }
}
```

{% endtab %}
{% endtabs %}

As you can see in the previous code snippet, the <mark style="color:orange;">`gridColumnDefInstance`</mark> is attached to the `onColumnDefInstance()` method. In the method definition, two more columns were added: **Email** and **Hire Date**.

{% hint style="info" %}
The <mark style="color:orange;">`gridColumnDefInstance`</mark> event is emitted after the `ngOnInit()` lifecycle hook of the <mark style="color:green;">`DikeGridComponent`</mark> component.
{% endhint %}

The following screenshot shows the output after adding the columns:

![Defining columns](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FJjeRdiFeM836Mwd5datk%2Fcolum-definitions-adding-columns.png?alt=media\&token=8270a2c4-a1ae-43c0-b1ee-dc6daebe8f0f)

By default, columns show a **context menu**. You can suppress this context menu for a specific column, all columns in a DikeGrid definition, or all DikeGrid instances.

{% hint style="success" %}
See the [Column Context Menu](/fundamentals/grid-structure/column-context-menu#suppress-the-column-context-menu) section for details.
{% endhint %}

## Columns Order

Every column definition has an order property. If this property is not specified,  the order of the columns depends on at which moment the columns are defined.

The following table shows the execution order the column definitions take place:

| Column Definition                                                                     |                                                                                                                                                                   |
| ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:orange;">`gridColumnDefInstance`</mark> event listener             | As it was told, this event is emitted after the `ngOnInit()` lifecycle hook of the <mark style="color:green;">`DikeGridComponent`</mark> component.               |
| HTML definition                                                                       | These columns are added in the `ngAfterContentInit()` lifecycle hook of the <mark style="color:green;">`DikeGridComponent`</mark> component.                      |
| <mark style="color:green;">`DikeGridComponent`</mark> instance queried from the view. | These columns are added in the `ngAfterViewInit()` lifecycle hook of the component that has the <mark style="color:green;">`DikeGridComponent`</mark> definition. |

{% hint style="info" %}
In every **Column Definition** execution, the columns are added in the order they are defined unless the order property is set.
{% endhint %}

### Specifying the order of the columns

Let's say we want the following order:&#x20;

1. Employee Id
2. Surname
3. Name
4. Gender
5. Age
6. Email
7. Hire Date

After setting the order property, the output will be the following:

![Setting the order of the columns](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F1ASJ2WKn4yK8mz9SRIA6%2Fcolum-definitions-columns-order.png?alt=media\&token=3d85dd01-bb98-421b-a95b-4cf98be0f5dc)

## Column types

Th DikeGrid Library defines four column types through a union type called <mark style="color:green;">`DikeColumnDataType`</mark>.

Depending on which type the column is, there is a default alignment used to display the content of the rows.

| DikeColumnDataType | Inner Type / Default alignment                                                                                                                                                                                                                                               | Class Definition                                                           |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `Text`             | <p>It holds data of type <mark style="color:green;"><code>string</code></mark>.<br>Default alignment <mark style="color:red;"><code>start</code></mark>.</p>                                                                                                                 | Instance of type <mark style="color:green;">`DikeTextColumnDef`</mark>.    |
| `Numeric`          | <p>It holds data of type <mark style="color:green;"><code>number</code></mark>.<br>Default alignment <mark style="color:red;"><code>end</code></mark>.</p>                                                                                                                   | Instance of type <mark style="color:green;">`DikeNumericColumnDef`</mark>. |
| `Date`             | <p>It hold data of type <mark style="color:green;"><code>string</code></mark>, <mark style="color:green;"><code>number</code></mark>, <mark style="color:green;"><code>Date</code></mark>.<br>Default alignment <mark style="color:red;"><code>center</code></mark>.</p>     | Instance of type <mark style="color:green;">`DikeDateColumnDef`</mark>.    |
| `Binary`           | <p>It holds data of type <mark style="color:green;"><code>string</code></mark>, <mark style="color:green;"><code>number</code></mark>, <mark style="color:green;"><code>boolean</code></mark>.<br>Default alignment <mark style="color:red;"><code>center</code></mark>.</p> | Instance of type <mark style="color:green;">`DikeBinaryColumnDef`</mark>.  |

{% hint style="info" %}
Content alignment comes from a union type called <mark style="color:green;">`ColumnAlignment`</mark>. This alignment could be set overwriting the default value.
{% endhint %}

When a column is defined via templating, the type must be provided through the <mark style="color:orange;">`dataType`</mark> input property. If the column is defined in code, the corresponding class instance must be created.

## Retrieving and Updating a field value

By default, every column defines functions for getting or setting its field value. For instance, consider a `Numeric` column, its default getter function is:

```typescript
getValue: GetterFn<T, number> = (entry: T): number => {
  return ((entry as { [key in keyof T]: T[key] })[this.fieldName]) as number;
};
```

And its default setter function is:

```typescript
setValue: SetterFn<T, number> = (entry: T, value: number): void => {
  (entry as { [key in keyof T]: T[key] })[this.fieldName] = value;
 };
```

These functions are of type <mark style="color:green;">`GetterFn<T, R>`</mark> and <mark style="color:green;">`SetterFn<T, R>`</mark> respectively. These default functions treat the received object of type `T` as a simple object, which means an object without any nested object.

Let's define the getter function for the <mark style="color:green;">`Employee`</mark> interface to retrieve the sales for March month.

```typescript
// Define the March Sales column:
const marchSales = new DikeNumericColumnDef<Employee>('marchSales', 'March Sales');
marchSales.width = 150;
marchSales.getValue = (entry: Employee): number => entry.sales.monthlySales['March'];
```

The result of adding the column **March Sales** is shown in the following image:

![Defining the getter function for the March Sales column](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FEtVQgET4OyuIQokFV7Ks%2Fcolum-definitions-getterFn.png?alt=media\&token=68373e0d-ec7c-436e-a600-0fc2ab5c7aa6)

As you can see, the `marchSales` field was defined but it does not exist. There is no `marchSales` field in the <mark style="color:green;">`Employee`</mark> interface, the value is in a nested object. Therefore, it is mandatory to define the getter function that retrieves the desired value.

If the column were **editable**, the corresponding setter function looks like the following:

```typescript
marchSales.setValue = (entry: Employee, value: number): void => {
    entry.sales.monthlySales['March'] = value;
};
```

## Templates for displaying field values

Every column has a default template for displaying its field value. This template depends on the column type because depending on the type the corresponding Angular Pipe is applied.

### Text and Binary columns

```markup
<!-- Template definition for Text columns -->
<ng-template #dikeTextDisplay let-entry="rowEntry" let-value="fieldValue">
  <p>{{ value }}</p>
</ng-template>

<!-- Template definition for Binary columns -->
<ng-template #dikeBinaryDisplay let-entry="rowEntry" let-value="fieldValue">
  <p>{{ value }}</p>
</ng-template>
```

As you can see, for `Text` and `Binary` columns no Angular Pipes are applied.

### Numeric and Date columns

```markup
<!-- Template definition for Numeric columns -->
<ng-template #dikeNumericDisplay let-entry="rowEntry" let-value="fieldValue">
  <p>{{ value | number }}</p>
</ng-template>

<!-- Template definition for Date columns -->
<ng-template #dikeDateDisplay let-entry="rowEntry" let-value="fieldValue">
  <p>{{ value | date:'shortDate' }}</p>
</ng-template>
```

{% hint style="info" %}
To see more on [Angular pipes](https://angular.io/guide/pipes) visit the official docs.
{% endhint %}

### Custom templates

You can define your own templates. If you notice when defining a template you always have available the **row entry** and the **field value**. Let's define custom templates for **Surname**, **Name**, **Gender**, **Hire Date,** and **March Sales**.

#### Surname and Name templates

The `uppercase` Angular Pipe for **Surname** and **Name** column templates was added. These templates are bound in the HTML definition.

{% code title="column-definitions.component.html" %}

```markup
<dike-grid-column
    fieldName="firstName"
    headerText="Name"
    dataType="Text"
    width="150"
    order="2"
    [displayTemplate]="firstName">

    <ng-template #firstName let-value="fieldValue">
        <p>{{ value | uppercase }}</p>
    </ng-template>
</dike-grid-column>

<dike-grid-column
    fieldName="lastName"
    headerText="Surname"
    dataType="Text"
    width="150"
    order="1"
    [displayTemplate]="lastName">

    <ng-template #lastName let-value="fieldValue">
        <p>{{ value | uppercase }}</p>
    </ng-template>
</dike-grid-column>
```

{% endcode %}

Both templates are assigned to the input property called <mark style="color:orange;">`displayTemplate`</mark>.

#### Gender template

See the following code snippet for the **Gender** column template definition:

{% tabs %}
{% tab title="column-definitions.component.html" %}

```markup
<!-- Gender column template is defined outside the dike-grid selector -->
<ng-template #gender let-value="fieldValue">
    <mat-icon [matTooltip]="value">{{ value }}</mat-icon>
</ng-template>
```

{% endtab %}

{% tab title="column-definitions.component.ts" %}

```typescript
@Component({
  selector: 'column-definitions',
  templateUrl: './column-definitions.component.html',
  styleUrls: ['./column-definitions.component.scss'],
  
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnDefinitionsComponent implements OnInit, AfterViewInit {
  // Retrieve the corresponding template:
  @ViewChild('gender', { read: TemplateRef }) genderTpl: TemplateRef<any>;

  constructor(
    private sampleData: SampleData) { }

  ngAfterViewInit(): void {
    // The DikeGridComponent<T> instance is available:
    Promise.resolve().then(() => {

      // Define the Gender column:
      const genderColumn = new DikeBinaryColumnDef<Employee>('gender', 'Gender');
      genderColumn.width = 110;
      genderColumn.order = 4;
      // Since genderTpl is available in this life cycle hook, just assign the template:
      genderColumn.displayTemplate = this.genderTpl;

      // Define the Age column:
      const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
      ageColumn.width = 85;
      ageColumn.order = 5;

      // Finally, add them to the DikeGridComponent<T> instance:
      this.dikeGrid.columnDef.addColumns([ genderColumn, ageColumn ]);
    });
  }
}
```

{% endtab %}
{% endtabs %}

As you can see the **Gender** column template is retrieved querying the component's view. Remember that queried elements are set before the `ngAfterViewInit` life cycle hook, and the **Gender** column is defined in the `ngAfterViewInit` life cycle hook, the **Gender** column template is assigned to its corresponding column property.

#### Hire Date and March Sales column templates

In the following code snippet is shown the column templates definitions:

{% tabs %}
{% tab title="column-definitions.component.html" %}

```markup
<!-- Column templates are defined outside the dike-grid selector -->
<ng-template #hireDate let-value="fieldValue">
    <p>{{ value | date:'fullDate' }}</p>
</ng-template>

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

{% endtab %}

{% tab title="column-definitions.component.ts" %}

```typescript
@Component({
  selector: 'column-definitions',
  templateUrl: './column-definitions.component.html',
  styleUrls: ['./column-definitions.component.scss'],
  
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnDefinitionsComponent implements OnInit, AfterViewInit {
  // Retrieve the corresponding template:
  @ViewChild('hireDate', { read: TemplateRef }) hireDateTpl: TemplateRef<any>;
  @ViewChild('marchSales', { read: TemplateRef }) marchSalesTpl: TemplateRef<any>;
  
  constructor(
    private sampleData: SampleData) { }

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 300;
    emailColumn.order = 6;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.order = 7;
    hireDateColumn.width = 215;
    hireDateColumn.displayTemplate = from(Promise.resolve().then(() => this.hireDateTpl));

    // Define the March Sales column:
    const marchSales = new DikeNumericColumnDef<Employee>('marchSales', 'March Sales');
    marchSales.order = 8;
    marchSales.width = 150;
    marchSales.getValue = (entry: Employee): number => entry.sales.monthlySales['March'];
    /**
     * Since Hire Date and March Sales columns are defined before the templates are ready
     * and available, an Observable is assigned waiting for the template availability.
     */
    marchSales.displayTemplate = from(Promise.resolve().then(() => this.marchSalesTpl));

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns([ emailColumn, hireDateColumn, marchSales ]);
  }
}
```

{% endtab %}
{% endtabs %}

For **Hire Date** and **March Sales** column templates, the `date` and `currency` Angular Pipes are used respectively.

For these columns, an **Observable** is assigned to <mark style="color:orange;">`displayTemplate`</mark> column property because both columns are defined in the <mark style="color:orange;">`gridColumnDefInstance`</mark> event callback. Column templates will be available until the `ngAfterViewInit` hook and, remember that the <mark style="color:orange;">`gridColumnDefInstance`</mark> event is emitted just after the **DikeGridComponent's** `ngOnInit` hook.

The following screenshot shows the output after defining the custom templates.

![Custom column templates](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FnqiS5LrVQ2eE6ioeV91p%2Fcolum-definitions-custom-templates.png?alt=media\&token=27c3f8da-c44a-4e17-a3b8-b3917d5204ce)

## Column Identifiers

All column identifiers are **UUIDs** and are used internally. If the user assigns them when defining columns, these are ignored.

The column identifiers are <mark style="color:orange;">`columnId`</mark>, <mark style="color:orange;">`slotId`</mark> and <mark style="color:orange;">`belongToGroup`</mark> properties.

## Summary

In this section, the main building blocks were covered. Columns are the main part for displaying data through a DikeGrid instance.&#x20;

### Complete code for this section

{% tabs %}
{% tab title="column-definitions.component.html" %}

```markup
<dike-grid #grid="dkgGrid" id="grid-col-def" height="500px"
    gridElevation
    striped
    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        order="2"
        [displayTemplate]="firstName">

        <ng-template #firstName let-value="fieldValue">
            <p>{{ value | uppercase }}</p>
        </ng-template>
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150"
        order="1"
        [displayTemplate]="lastName">

        <ng-template #lastName let-value="fieldValue">
            <p>{{ value | uppercase }}</p>
        </ng-template>
    </dike-grid-column>

</dike-grid>

<ng-template #gender let-value="fieldValue">
    <mat-icon [matTooltip]="value">{{ value }}</mat-icon>
</ng-template>

<ng-template #hireDate let-value="fieldValue">
    <p>{{ value | date:'fullDate' }}</p>
</ng-template>

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

{% endtab %}

{% tab title="column-definitions.component.ts" %}

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

import { DikeGridComponent, DikeGridDataSourceInput, DikeBinaryColumnDef,
    DikeNumericColumnDef, DikeGridColumnDef, DikeTextColumnDef, DikeDateColumnDef
} from '@dikesoft/angular-data-grid';

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

@Component({
  selector: 'column-definitions',
  templateUrl: './column-definitions.component.html',
  styleUrls: ['./column-definitions.component.scss'],
  
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnDefinitionsComponent implements OnInit, AfterViewInit {

  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;
  // Retrieve the corresponding template:
  @ViewChild('gender', { read: TemplateRef }) genderTpl: TemplateRef<any>;
  @ViewChild('hireDate', { read: TemplateRef }) hireDateTpl: TemplateRef<any>;
  @ViewChild('marchSales', { read: TemplateRef }) marchSalesTpl: TemplateRef<any>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;

  constructor(
    private sampleData: SampleData) { }

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 300;
    emailColumn.order = 6;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.order = 7;
    hireDateColumn.width = 215;
    hireDateColumn.displayTemplate = from(Promise.resolve().then(() => this.hireDateTpl));

    // Define the March Sales column:
    const marchSales = new DikeNumericColumnDef<Employee>('marchSales', 'March Sales');
    marchSales.order = 8;
    marchSales.width = 150;
    marchSales.getValue = (entry: Employee): number => entry.sales.monthlySales['March'];
    /**
     * Since Hire Date and March Sales columns are defined before the templates are ready
     * and available, an Observable is assigned waiting for the template availability.
     */
    marchSales.displayTemplate = from(Promise.resolve().then(() => this.marchSalesTpl));

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns([ emailColumn, hireDateColumn, marchSales ]);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
  }

  ngAfterViewInit(): void {
    // The DikeGridComponent<T> instance is available:
    Promise.resolve().then(() => {

      // Define the Gender column:
      const genderColumn = new DikeBinaryColumnDef<Employee>('gender', 'Gender');
      genderColumn.width = 110;
      genderColumn.order = 4;
      // Since genderTpl is available in this life cycle hook, just assign the template:
      genderColumn.displayTemplate = this.genderTpl;

      // Define the Age column:
      const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
      ageColumn.width = 85;
      ageColumn.order = 5;

      // Finally, add them to the DikeGridComponent<T> instance:
      this.dikeGrid.columnDef.addColumns([ genderColumn, ageColumn ]);
    });
  }
}
```

{% endtab %}
{% endtabs %}


# Column Grouping

This section describes creating column groups in the component's HTML definition or the TypeScript class file.

## Live example

{% hint style="success" %}
[Column Grouping](https://demos.dikesoft.com/dk-grid/column/grouping) live example.
{% endhint %}

## Groups Definition

To create a column group, you nest a column definition under another column definition. You can nest column definitions as many as you need.

Let us define the following column groups:

1. Firstly, We have two main groups: **Personal Info** and **Employee Info** column groups.
2. Under the **Personal Info** group, we have the **Complete Name** column group, the **Gender** and **Age** columns.
3. Under the **Complete Name** column group, we have **Name** and **Surname** columns.
4. Lastly, under the **Employee Info**, we have the **Employee Id**, **Email**, and **Hire Date** columns.

In code, we have:

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid id="grid-col-grouping" height="600px"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="1">

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

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150">
            </dike-grid-column>

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

        </dike-grid-column>

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

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

    </dike-grid-column>

</dike-grid>
```

{% endtab %}

{% tab title="column-grouping.component.ts" %}

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

import { Subscription } from 'rxjs';

import { DikeDateColumnDef, DikeGridColumnDef, 
  DikeGridDataSourceInput, DikeTextColumnDef, DikeGroupColumnDef 
} 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: 'column-grouping',
  templateUrl: './column-grouping.component.html',
  styleUrls: ['./column-grouping.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnGroupingComponent implements OnInit, OnDestroy {

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 250;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.width = 120;

    // Define the Employee Id column:
    const employeeIdColumn = new DikeTextColumnDef<Employee>('employeeId', 'Employee Id');
    employeeIdColumn.width = 350;

    // Lastly, define the Employee Info column group:
    const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
    employeeInfo.order = 2;

    // Add columns to the column group:
    employeeInfo.children = [ employeeIdColumn, emailColumn, hireDateColumn ];

    // Then, add the colum group to the DikeGridComponent instance:
    columnDef.addColumns(employeeInfo);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

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

```

{% endtab %}
{% endtabs %}

As you can see in the previous definition, we create the column groups in the **HTML definition** and **TypeScript code**, as we define any column.

You must not provide the `dataType` property because column groups can hold any number of columns, even column groups.

![Column Grouping definition](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FsasD7AtNCuvpF5zOFCjR%2Fcolumn-grouping-definition.png?alt=media\&token=590ea8a2-a30e-4897-a8f2-4d18916cb8a2)

{% hint style="danger" %}
Be aware when you create a column group in the TypeScritpt code, the children property is null.
{% endhint %}

## Group Status

Any group could be ***open*** or ***closed***. The property `displayStatus` stores the group status.

If you want a group could open or close, its child columns must tell the grid which columns are visible in closed status.

Let us define another column under the **Employee Info** group. This column will be visible when the **Employee Info** column group is closed, displaying the **Total Sales**.

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid>...</dike-grid>
<ng-template #totalSales let-value="fieldValue">
    <p>{{ value | currency }}</p>
</ng-template>
```

{% endtab %}

{% tab title="column-grouping.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {    
    // Define the Total Sales column:
    const totalSalesColumn = new DikeNumericColumnDef<Employee>('totalSales', 'Total Sales');
    totalSalesColumn.width = 150;
    totalSalesColumn.displayTemplate = from(Promise.resolve().then(() => this.totalSalesTpl));
    totalSalesColumn.displayOn = 'closed';
    
    // Lastly, define the Employee Info group:
    const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
    employeeInfo.order = 2;
    
    // Add columns to the column group:
    employeeInfo.children = [ employeeIdColumn, emailColumn, hireDateColumn, totalSalesColumn ];
    
    // Then, add the colum group to the DikeGridComponent instance:
    columnDef.addColumns(employeeInfo);
}
```

{% endtab %}
{% endtabs %}

In the previous code, we have defined the following:

1. Firstly, we determined the template for displaying the total sales field.
2. Then, we create the actual column definition for **Total Sales**. The `displayOn` property is equal to **closed**, meaning that the user will see this value when the **Employee Info** group is closed.

![Column Group Status](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F3K0N4MloG6F2A6XfifSP%2Fcolumn-grouping-open-close.gif?alt=media\&token=711be263-e859-4ea5-8501-9eed08dfe872)

{% hint style="info" %}
The groups that can open or close will have a **right** or **left** arrow, indicating that they can change their status.
{% endhint %}

{% hint style="info" %}
If you do not specify the `displayOn` property, the default value will be ***open***.
{% endhint %}

### Initial Group Status

By default, the initial status of a group is ***open***. However, you can change this value by providing the ***closed*** value to the `displayStatus` property.

Let us change the initial status for the **Employee Info** group to a closed value.

{% code title="column-grouping.component.ts" %}

```typescript
// Lastly, define the Employee Info group:
const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
employeeInfo.order = 2;
employeeInfo.displayStatus = 'closed';
```

{% endcode %}

When you launch the HTML containing the DikeGrid definition, the **Employee Info** group will appear closed.

{% hint style="warning" %}
If you set a group as ***closed*** and none of the child columns has the `displayOn` property to a ***closed*** value, the group status will be ***open***.

If you set a group as ***open*** and all the child columns have the `displayOn` property to a ***closed*** value, the group status will be ***open***.
{% endhint %}

### Group Status Event

When a column group changes its status, the DikeGrid instance emits an event. The name of the event is `columnGroupStatusChange`.

Let us listen to this group status event from the DikeGrid instance directly.

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid id="grid-col-grouping" height="600px"
    (columnGroupStatusChange)="onColumnGroupStatusChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="column-grouping.component.ts" %}

```typescript
onColumnGroupStatusChange(columnGroup: DikeGroupColumnDef): void {
    console.log('onColumnGroupStatusChange: ', columnGroup);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
You can listen to this event from the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef) service through the `columnGroupStatusChange` property.
{% endhint %}

Open the **Personal Info** group and see the event information in the **dev console**.

## Column Chooser Menu

When you navigate to the **Column Chooser Menu** and open the **Employee Info** group, you will see a circle with an **accent** color next to the **Total Sales** column.

The circle next to the **Total Sales** column indicates that the column is visible when the parent group is ***closed***.

![DisplayOn indicator](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fgl8iMu7aa7NM9sMsJfvq%2Fcolumn-grouping-circle-indicator.png?alt=media\&token=4a824eee-142d-4511-a091-fc3126c45d59)

## Summary

In this section, we explored column groups creation and its status. Then, we listen to the event sent when this status has changed and spot a column displayed in a closed position.

### Complete code for this section

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid id="grid-col-grouping" height="600px"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    (columnGroupStatusChange)="onColumnGroupStatusChange($event)"
    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="1">

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

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150">
            </dike-grid-column>

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

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

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

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

{% endtab %}

{% tab title="column-grouping.component.ts" %}

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

import { from, Subscription } from 'rxjs';

import { DikeDateColumnDef, DikeGridColumnDef, 
  DikeGridDataSourceInput, DikeTextColumnDef, DikeGroupColumnDef, DikeNumericColumnDef 
} 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: 'column-grouping',
  templateUrl: './column-grouping.component.html',
  styleUrls: ['./column-grouping.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnGroupingComponent implements OnInit, OnDestroy {
  // Retrieve the corresponding templates:
  @ViewChild('totalSales', { read: TemplateRef }) totalSalesTpl: TemplateRef<any>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 250;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.width = 120;

    // Define the Employee Id column:
    const employeeIdColumn = new DikeTextColumnDef<Employee>('employeeId', 'Employee Id');
    employeeIdColumn.width = 350;

    // Define the Total Sales column:
    const totalSalesColumn = new DikeNumericColumnDef<Employee>('totalSales', 'Total Sales');
    totalSalesColumn.width = 150;
    totalSalesColumn.displayTemplate = from(Promise.resolve().then(() => this.totalSalesTpl));
    totalSalesColumn.displayOn = 'closed';

    // Lastly, define the Employee Info group:
    const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
    employeeInfo.order = 2;
    employeeInfo.displayStatus = 'closed';

    // Add columns to the column group:
    employeeInfo.children = [ employeeIdColumn, emailColumn, hireDateColumn, totalSalesColumn ];

    // Then, add the colum group to the DikeGridComponent instance:
    columnDef.addColumns(employeeInfo);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

  onColumnGroupStatusChange(columnGroup: DikeGroupColumnDef): void {
    console.log('onColumnGroupStatusChange: ', columnGroup);
  }
  
  private setChangeGridPropertiesSubscription(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
    this.changeGridPropertiesSubscription = this.gridConfig.configChange.subscribe((props: DikeGridProperties) => {
      this.gridProperties = props;
      this.cdr.markForCheck();
    });
  }
}

```

{% endtab %}
{% endtabs %}


# Column Sizing

After setting the initial column size, this section describes how to change the column width dynamically.

## Live example

{% hint style="success" %}
[Column Sizing](https://demos.dikesoft.com/dk-grid/column/sizing) live example.
{% endhint %}

## Column width

When you define a column, this column will have an initial value for the <mark style="color:orange;">`width`</mark> property.

{% hint style="info" %}
If <mark style="color:orange;">`width`</mark> property is not defined, the **default** value will be **200**. The with's unit is **pixels**.
{% endhint %}

## Changing the column width through the UI

### Minimum and Maximum column width

If you want to change the width of a column, you must provide the <mark style="color:orange;">`minWidth`</mark> and <mark style="color:orange;">`maxWidth`</mark> properties.

{% hint style="info" %}
If you do not provide the <mark style="color:orange;">`minWidth`</mark> and <mark style="color:orange;">`maxWidth`</mark> properties, they will take the value of the <mark style="color:orange;">`width`</mark> property.
{% endhint %}

{% hint style="success" %}
If you want the user to change the column width through the UI, mark the column as **resizable**.
{% endhint %}

To see these properties in action, consider the following initial configuration:

{% tabs %}
{% tab title="column-sizing.component.html" %}

```markup
<dike-grid id="grid-col-sizing" height="500px"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"
    
    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        order="2"
        resizable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150"
        minWidth="50"
        maxWidth="300"
        order="1"
        resizable>
    </dike-grid-column>

</dike-grid>
```

{% endtab %}

{% tab title="column-sizing.component.ts" %}

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

import { DikeGridColumnDef, DikeGridDataSourceInput, 
  DikeTextColumnDef 
} from '@dikesoft/angular-data-grid';

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

@Component({
  selector: 'column-sizing',
  templateUrl: './column-sizing.component.html',
  styleUrls: ['./column-sizing.component.scss'],
  
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnSizingComponent implements OnInit, OnDestroy {

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;
  
  private columnDef: DikeGridColumnDef<Employee>;
  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;
  
  constructor(
    private cdr: ChangeDetectorRef,
    private gridConfig: DikeGridConfig,
    private sampleData: SampleData) { }

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 300;
    emailColumn.order = 3;

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns(emailColumn);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }
  
  ngOnDestroy(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
  }
  
  private setChangeGridPropertiesSubscription(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
    this.changeGridPropertiesSubscription = this.gridConfig.configChange.subscribe((props: DikeGridProperties) => {
      this.gridProperties = props;
      this.cdr.markForCheck();
    });
  }
}
```

{% endtab %}
{% endtabs %}

With the initial configuration, we have the following:

1. The **Employee Id** column does not define its <mark style="color:orange;">`width`</mark> nor its <mark style="color:orange;">`resizable`</mark> property.
2. You can change the **Surname** column width through the UI because the configuration sets the <mark style="color:orange;">`resizable`</mark> property and the <mark style="color:orange;">`minWidth`</mark> and <mark style="color:orange;">`maxWidth`</mark> properties have a different value of <mark style="color:orange;">`width`</mark> property.
3. The **Name** column is **resizable**, and the <mark style="color:orange;">`minWidth`</mark> and <mark style="color:orange;">`maxWidth`</mark> properties will take the same value as the <mark style="color:orange;">`width`</mark> property because this configuration does not set their values. Therefore, you will not be able to change the **Name** column width.
4. Finally, for the **Email** column, only the <mark style="color:orange;">`width`</mark> property is set.

As you can see in the following gif, you can only change the **Surname** column width but not for the **Name** column.

![Changing the column width through the UI](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4vuMChgiT7zWQeQvfrX7%2Fcolumn-sizing-change-width-ui.gif?alt=media\&token=bae1c593-7249-4541-abbf-ae2b8eeb11b4)

{% hint style="info" %}
When a column is **resizable**, the cursor changes to **col-resize**, and when you start resizing it, the DikeGrid draws a **vertical line**. The color of the line will be the **accent** color.
{% endhint %}

### Changing the width through the vertical row lines

If the vertical lines per row are visible, you can resize a column using them.

Let us make the vertical lines per row visible. First, open the [Floating Configuration Panel](/#floating-configuration-panel), then check the **Vertical Row Lines** checkbox from the **Appearance** group.

![Floating Configuration Panel - Appearance](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FPLCFDpJj3qDGOZjCXAVq%2Fgrid-structure-vertical-row-lines-enabled.png?alt=media\&token=4a6837bb-eb98-4d2e-9a66-8d696401b547)

In the following gif, you can see how dragging a vertical line changes the size of the column.

![Changing the column width through the vertical row lines](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FTQgK3IqBy6cyqoA7m1Ce%2Fcolumn-sizing-change-width-vertical-lines.gif?alt=media\&token=b38af87c-8856-404c-8042-4e83c5a0a299)

{% hint style="info" %}
Remember, columns displayed in the **left** or **center** panel have their **gutter-line** at their **right**. See the [Grid Structure - Gutters](/fundamentals/grid-structure/gutters#who-does-the-vertical-gutters-own) section.
{% endhint %}

### Columns in the right panel

Columns displayed in the right panel have their gutter-line at their left. Therefore, these columns change their size in an inverse direction compared to columns displayed in the left or center panel.

When you resize a column in the right panel to increase its width, you must move its gutter-line to the left.

If you want to decrease the column width, move the gutter-line to the right.

Let us create two more columns in the right panel, **Hire Date** and **Age** columns.

{% tabs %}
{% tab title="column-sizing.component.html" %}

```markup
<dike-grid id="grid-col-sizing" height="500px"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">
    
    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        minWidth="50"
        maxWidth="300"
        order="1"
        resizable
        panel="rightPanel">
    </dike-grid-column>

</dike-grid>
```

{% endtab %}

{% tab title="column-sizing.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
  // Define the Email column:
  const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
  emailColumn.width = 300;
  emailColumn.order = 3;
  
  // Then, add the colums to the DikeGridComponent instance:
  columnDef.addColumns(emailColumn);
  
  // Define the Age column to be displayed in the right Panel:
  const ageColumn = new DikeTextColumnDef<Employee>('age', 'Age');
  ageColumn.width = 85;
  ageColumn.maxWidth = 150;
  ageColumn.order = 2;
  ageColumn.resizable = true;
  ageColumn.panel = 'rightPanel';
  
  // Add the Age column:
  columnDef.addColumns(ageColumn);
  
  // Grab the DikeGridColumnDef<T> instance:
  this.columnDef = columnDef;
  
  // Once all the columns have been added:
  Promise.resolve().then(() => {
    // Find the Employee Id column:
    const employeeId = this.columnDef.findColumn(column => column.fieldName === 'employeeId');
  
    // Set the initial values in the text boxes:
    if (!!employeeId && isDikeDataColumnDef(employeeId)) {
      this.columnSizingForm.patchValue({
        width: employeeId.width,
        minWidth: employeeId.minWidth,
        maxWidth: employeeId.maxWidth,
        resizable: employeeId.resizable
      });
    }
  });
}
```

{% endtab %}
{% endtabs %}

![Resizing a column in the right panel](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F7Ho5TvuMoq1VcByT4dMC%2Fcolumn-sizing-change-width-right-panel-ui.gif?alt=media\&token=96446c42-3dd5-4c54-9123-9177bf9a3edc)

#### Dragging the panel division line

When you drag the line that divides two adjacent panels, all columns resize evenly. The resizing operation finishes until all columns reach their minimum or maximum width.

![Dragging the panel division line](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FOpO7w4Bt9K12GIpoGS5H%2Fcolumn-sizing-change-width-right-panel-line-ui.gif?alt=media\&token=f77bd3a0-6c25-405b-b7f7-d11bbff626c5)

The same occurs for columns displayed in the left panel.

{% hint style="success" %}
You can change the width of the panel division line when the panel is being resized by providing an Injection Token called <mark style="color:blue;">`DIVISION_PANEL_LINE_WIDTH_RESIZING`</mark>. By default, the width is **5 pixels** in width.
{% endhint %}

## DikeGrid Column API

You can change the size properties using the API from the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef)service instance.

{% hint style="success" %}
In the [Column Definition](/columns/column-definitions#dikegridcolumndef-less-than-t-greater-than-service) section, see how to grab the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef) instance from the corresponding [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent)<mark style="color:green;">.</mark>
{% endhint %}

### Employee Id column

Since the **Employee Id** column is not resizable and its <mark style="color:orange;">`width`</mark> property is not defined, let us add the UI controls and the corresponding code to change its width using the DikeGrid API.

{% code title="column-sizing.component.html" %}

```markup
<div class="p-4 leading-6 text-secondary"><strong>Employee Id</strong></div>
<form [formGroup]="columnSizingForm"
    class="flex flex-col p-4 overflow-hidden">
    <div class="flex flex-row flex-wrap items-center">
        <mat-form-field class="flex-auto pr-3">
            <mat-label>Width</mat-label>
            <input matInput
                type="number"
                formControlName="width">

            <mat-hint align="end">Numeric value</mat-hint>
            <mat-error *ngIf="getControl('width') as widthControl">
                <div *ngIf="widthControl.errors?.['required']">Required</div>
                <div *ngIf="widthControl.errors?.['min']">Min value 25</div>
                <div *ngIf="widthControl.errors?.['max']">Max value 600</div>
            </mat-error>
        </mat-form-field>

        <mat-form-field class="flex-auto pr-3">
            <mat-label>Min width</mat-label>
            <input matInput
                type="number"
                formControlName="minWidth">

            <mat-hint align="end">Numeric value</mat-hint>
            <mat-error *ngIf="getControl('minWidth') as minWidthControl">
                <div *ngIf="minWidthControl.errors?.['min']">Min value 25</div>
                <div *ngIf="minWidthControl.errors?.['max']">Max value 600</div>
            </mat-error>
        </mat-form-field>

        <mat-form-field class="flex-auto pr-3">
            <mat-label>Max width</mat-label>
            <input matInput
                type="number"
                formControlName="maxWidth">

            <mat-hint align="end">Numeric value</mat-hint>
            <mat-error *ngIf="getControl('maxWidth') as maxWidthControl">
                <div *ngIf="maxWidthControl.errors?.['min']">Min value 25</div>
                <div *ngIf="maxWidthControl.errors?.['max']">Max value 600</div>
            </mat-error>
        </mat-form-field>

        <div class="flex-auto flex flex-row justify-around items-center">
            <button mat-raised-button
                class="w-32"
                color="primary"
                [disabled]="!columnSizingForm.valid"
                (click)="onChangeColumnSize()">Apply
            </button>
            <mat-checkbox class="w-24"
                formControlName="resizable"
                (change)="onResizableChange($event)">Resizable
            </mat-checkbox>
        </div>
    </div>
</form>
```

{% endcode %}

The previous <mark style="color:red;">`form`</mark> is added above the <mark style="color:red;">`dike-grid`</mark> selector generating the following output:

![UI for changing the Employee Id column width](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FmuPmBv0xmxyXbGe4zDbR%2Fcolum-sizing-employeeId-width.png?alt=media\&token=2f1686b4-192b-486e-8310-2959987acd3c)

Now, you can change the **Employee Id** column's width, min-width, and max-width. If you click on the **checkbox** next to the Apply button, you can **mark** the Employee Id column as **resizable**.

#### Changing the width using the API

Consider the following code:

{% tabs %}
{% tab title="column-sizing.component.html" %}

```markup
<div class="flex-auto flex flex-row justify-around items-center">
    <button mat-raised-button
        class="w-32"
        color="primary"
        [disabled]="!columnSizingForm.valid"
        (click)="onChangeColumnSize()">Apply
    </button>
    <mat-checkbox class="w-24"
        formControlName="resizable"
        (change)="onResizableChange($event)">Resizable
    </mat-checkbox>
</div>
```

{% endtab %}

{% tab title="column-sizing.component.ts" %}

```typescript
onChangeColumnSize(): void {
  // Search the employeeId column:
  const targetColumn = this.columnDef.findColumn(column => column.fieldName === 'employeeId');

  if (!!targetColumn && isDikeDataColumnDef(targetColumn)) {
    // If the column is found:
    const width: number = this.columnSizingForm.get('width').value as number;
    const minWidth: number | null = this.columnSizingForm.get('minWidth').value;
    const maxWidth: number | null = this.columnSizingForm.get('maxWidth').value;

    // Change the column size:
    this.columnDef.changeColumnWidth(targetColumn, { width, minWidth, maxWidth });
  }
}
```

{% endtab %}
{% endtabs %}

Once the input elements pass the validators, the button can invoke the callback method. Inside the callback definition:

1. Firstly, you must find the column to be modified.
2. Notice how the found column instance is narrowing to a reference of type [<mark style="color:green;">`DikeDataColumnDef`</mark>](/reference/classes/columns#dikedatacolumndef-less-than-t-r-greater-than), due to the `findColumn()` method returns a reference of type [<mark style="color:green;">`DikeColumnDef`</mark>](/reference/classes/columns#dikecolumndef).
3. The code uses the `changeColumnWidth()` method to pass the column instance and the size properties, where only the width is mandatory.

{% hint style="info" %}
Internally, the [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent) validates the **width**, **minWidth**, and **maxWidth** arguments. For instance, if minWidth is greater than the width, the width value is assigned to minWidth.
{% endhint %}

{% hint style="info" %}
Despite the <mark style="color:orange;">`resizable`</mark> property for the **Employee Id** column is not set, the size properties of the column can be changed using the API.
{% endhint %}

#### Setting the resizable property

Remember that the <mark style="color:orange;">`resizable`</mark> property allows the user to change the width of a column or column group.

The following code snippet is the callback attached to the **checkbox change Event**.

{% code title="column-sizing.component.ts" %}

```typescript
onResizableChange(value: MatCheckboxChange): void {
  // Search the employeeId column:
  const targetColumn = this.columnDef.findColumn(column => column.fieldName === 'employeeId');

  if (!!targetColumn) {
    // If the column is found, change its resizable property:
    this.columnDef.setColumnResizable(targetColumn, value.checked);
  }
}
```

{% endcode %}

Once again, the target column must be found, in this case. The method `setColumnResizable()` marks the **Employee Id** column as **resizable**.

![Resizing the Employee Id column after setting it as resizable](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FbLrO4vNAHWL2DWY9RQUi%2Fcolumn-sizing-change-width-after-api-ui.gif?alt=media\&token=3679b0a8-0d1d-476f-9918-b6dfffe9f9dd)

The previous output shows how the **Employee Id** column is resized after setting it as **resizable**. We must have changed the <mark style="color:orange;">`minWidth`</mark>, or <mark style="color:orange;">`maxWidth`</mark> properties as well.

{% hint style="info" %}
Even though a column is marked as **resizable**, if the <mark style="color:orange;">`minWidth`</mark> and <mark style="color:orange;">`maxWidth`</mark> properties have the same value as the <mark style="color:orange;">`width`</mark> property, the column will not resize using the UI.
{% endhint %}

## Column Sizing Event

If you notice, the last output reports the changes in the <mark style="color:orange;">`width`</mark> property of the **Employee Id** column when this one is being resized.

The [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent) emits an Event called `columnSizeChange`. This event is emitted every time a data column changes its size.

{% hint style="info" %}
The [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef)service also emits the `columnSizeChange` event.
{% endhint %}

{% tabs %}
{% tab title="column-sizing.component.html" %}

```markup
<dike-grid id="grid-col-sizing" height="500px"
    (columnSizeChange)="onColumnSizeChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="column-sizing.component.ts" %}

```typescript
onColumnSizeChange(column: DikeDataColumnDef<Employee, unknown>): void {
  if (column.fieldName === 'employeeId') {
    this.columnSizingForm.patchValue({
      width: column.width,
      minWidth: column.minWidth,
      maxWidth: column.maxWidth
    });
  }
}
```

{% endtab %}
{% endtabs %}

Since the `columnSizeChange` event sends any column that has changed in width, the if statement gives us the **Employee Id** column.

## Resizing Groups

By definition, a column group's width equals the sum of its child columns' width. Therefore, a column group does not have a <mark style="color:orange;">`width`</mark> property.

All descendant columns, marked as **resizable**, will change in width when resizing a group.

{% hint style="info" %}
The `columnSizeChange` event does not send the group itself nor the descendant groups. Instead, it only **sends** the descendant **data columns**.
{% endhint %}

Let us define the following configuration:

1. Firstly, We have a primary group named **Personal Info**.
2. Under the **Personal Info** group, we have the **Complete Name** column group, the **Gender** and **Age** columns.
3. Finally, we have **Name** and **Surname** columns under the **Complete Name** group.
4. Except for the **Complete Name** group, all columns and column groups are resizable.

{% code title="column-sizing.component.ts" %}

```markup
<dike-grid id="grid-col-sizing" height="500px">
    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="0"
        resizable>
    
        <dike-grid-column
            fieldName="completeNameGroup"
            headerText="Complete Name">
    
            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>
    
            <dike-grid-column
                fieldName="lastName"
                headerText="Surname"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>
        </dike-grid-column>
    
        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110"
            minWidth="50"
            maxWidth="200"
            resizable>
        </dike-grid-column>
    
        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85"
            minWidth="50"
            maxWidth="120"
            resizable>
        </dike-grid-column>
    </dike-grid-column>
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
Column groups marked as **resizable** can change in width only from the UI.
{% endhint %}

![Resizing Groups](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FcwXGpUnnS37MsyCu8Q6A%2Fgroup-sizing.gif?alt=media\&token=4122e784-d88e-485c-af42-4015f509d255)

As you can see in the previous output:

1. When resizing the **Personal Info** group, the operation draws a vertical line with an **accent** color until the columns.
2. The resize process draws the line only into the column's boundary for the **Surname** column.

{% hint style="success" %}
Remember, for resizing a column or a group, you must grab the gutter-line for the column. See the [Grid Structure - Gutters](/fundamentals/grid-structure/gutters#who-does-the-vertical-gutters-own) section for more details.
{% endhint %}

## Summary

Once you define a column at the creation phase, you can change its width at execution time, either from the **UI** or the **API**. Columns that are resizing through the UI must be **resizable**.

The DikeGrid instance only **sends** the changed **data columns** when the resizing operation occurs, not column groups.

### Complete code for this section

{% tabs %}
{% tab title="column-sizing.component.html" %}

```markup
<div class="p-4 leading-6 text-secondary"><strong>Employee Id</strong></div>
<form [formGroup]="columnSizingForm"
    class="flex flex-col p-4 overflow-hidden">
    <div class="flex flex-row flex-wrap items-center">
        <mat-form-field class="flex-auto pr-3">
            <mat-label>Width</mat-label>
            <input matInput
                type="number"
                formControlName="width">

            <mat-hint align="end">Numeric value</mat-hint>
            <mat-error *ngIf="getControl('width') as widthControl">
                <div *ngIf="widthControl.errors?.['required']">Required</div>
                <div *ngIf="widthControl.errors?.['min']">Min value 25</div>
                <div *ngIf="widthControl.errors?.['max']">Max value 600</div>
            </mat-error>
        </mat-form-field>

        <mat-form-field class="flex-auto pr-3">
            <mat-label>Min width</mat-label>
            <input matInput
                type="number"
                formControlName="minWidth">

            <mat-hint align="end">Numeric value</mat-hint>
            <mat-error *ngIf="getControl('minWidth') as minWidthControl">
                <div *ngIf="minWidthControl.errors?.['min']">Min value 25</div>
                <div *ngIf="minWidthControl.errors?.['max']">Max value 600</div>
            </mat-error>
        </mat-form-field>

        <mat-form-field class="flex-auto pr-3">
            <mat-label>Max width</mat-label>
            <input matInput
                type="number"
                formControlName="maxWidth">

            <mat-hint align="end">Numeric value</mat-hint>
            <mat-error *ngIf="getControl('maxWidth') as maxWidthControl">
                <div *ngIf="maxWidthControl.errors?.['min']">Min value 25</div>
                <div *ngIf="maxWidthControl.errors?.['max']">Max value 600</div>
            </mat-error>
        </mat-form-field>

        <div class="flex-auto flex flex-row justify-around items-center">
            <button mat-raised-button
                class="w-32"
                color="primary"
                [disabled]="!columnSizingForm.valid"
                (click)="onChangeColumnSize()">Apply
            </button>
            <mat-checkbox class="w-24"
                formControlName="resizable"
                (change)="onResizableChange($event)">Resizable
            </mat-checkbox>
        </div>
    </div>
</form>

<dike-grid id="grid-col-sizing" height="500px"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    (columnSizeChange)="onColumnSizeChange($event)"
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        order="2"
        resizable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150"
        minWidth="50"
        maxWidth="300"
        order="1"
        resizable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="0"
        resizable>

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

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>

            <dike-grid-column
                fieldName="lastName"
                headerText="Surname"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>
        </dike-grid-column>

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110"
            minWidth="50"
            maxWidth="200"
            resizable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85"
            minWidth="50"
            maxWidth="120"
            resizable>
        </dike-grid-column>
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        minWidth="50"
        maxWidth="300"
        order="1"
        resizable
        panel="rightPanel">
    </dike-grid-column>
</dike-grid>
```

{% endtab %}

{% tab title="column-sizing.component.ts" %}

```typescript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { MatCheckboxChange } from '@angular/material/checkbox';

import { Subscription } from 'rxjs';

import { DikeDataColumnDef, DikeGridColumnDef, DikeGridDataSourceInput,
  DikeTextColumnDef, isDikeDataColumnDef } from '@dikesoft/angular-data-grid';

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

import { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnSizingComponent implements OnInit, OnDestroy {

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  columnSizingForm: FormGroup;
  gridProperties: DikeGridProperties;

  private columnDef: DikeGridColumnDef<Employee>;
  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 300;
    emailColumn.order = 3;

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns(emailColumn);

    // Define the Age column to be displayed in the right Panel:
    const ageColumn = new DikeTextColumnDef<Employee>('age', 'Age');
    ageColumn.width = 85;
    ageColumn.maxWidth = 150;
    ageColumn.order = 2;
    ageColumn.resizable = true;
    ageColumn.panel = 'rightPanel';

    // Add the Age column:
    columnDef.addColumns(ageColumn);

    // Grab the DikeGridColumnDef<T> instance:
    this.columnDef = columnDef;

    // Once all the columns have been added:
    Promise.resolve().then(() => {
      // Find the Employee Id column:
      const employeeId = this.columnDef.findColumn(column => column.fieldName === 'employeeId');

      // Set the initial values in the text boxes:
      if (!!employeeId && isDikeDataColumnDef(employeeId)) {
        this.columnSizingForm.patchValue({
          width: employeeId.width,
          minWidth: employeeId.minWidth,
          maxWidth: employeeId.maxWidth,
          resizable: employeeId.resizable
        });
      }
    });
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Creating the form that binds the UI ontrols:
    this.columnSizingForm = this.createMainForm();
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

  getControl(formControlName: string): AbstractControl {
    return this.columnSizingForm.get(formControlName);
  }

  onColumnSizeChange(column: DikeDataColumnDef<Employee, string>): void {
    if (column.fieldName === 'employeeId') {
      this.columnSizingForm.patchValue({
        width: column.width,
        minWidth: column.minWidth,
        maxWidth: column.maxWidth
      });
    }
  }

  onResizableChange(value: MatCheckboxChange): void {
    // Search the employeeId column:
    const targetColumn = this.columnDef.findColumn(column => column.fieldName === 'employeeId');

    if (!!targetColumn) {
      // If the column is found, change its resizable property:
      this.columnDef.setColumnResizable(targetColumn, value.checked);
    }
  }

  onChangeColumnSize(): void {
    // Search the employeeId column:
    const targetColumn = this.columnDef.findColumn(column => column.fieldName === 'employeeId');

    if (!!targetColumn && isDikeDataColumnDef(targetColumn)) {
      // If the column is found:
      const width: number = this.columnSizingForm.get('width').value as number;
      const minWidth: number | null = this.columnSizingForm.get('minWidth').value;
      const maxWidth: number | null = this.columnSizingForm.get('maxWidth').value;

      // Change the column size:
      this.columnDef.changeColumnWidth(targetColumn, { width, minWidth, maxWidth });
    }
  }

  private createMainForm(): FormGroup {
    const mainForm = this.formBuilder.group({
        resizable: [false]
    });

    const widthControl = new FormControl('',
        [Validators.required, Validators.min(25), Validators.max(600), Validators.pattern('^[0-9]*$')]);
    widthControl.markAllAsTouched();

    const minWidthControl = new FormControl('',
        [Validators.min(25), Validators.max(600), Validators.pattern('^[0-9]*$')]);
    minWidthControl.markAsTouched();

    const maxWidthControl = new FormControl('',
        [Validators.min(25), Validators.max(600), Validators.pattern('^[0-9]*$')]);
    maxWidthControl.markAllAsTouched();

    mainForm.addControl('width', widthControl);
    mainForm.addControl('minWidth', minWidthControl);
    mainForm.addControl('maxWidth', maxWidthControl);

    return mainForm;
  }

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

```

{% endtab %}
{% endtabs %}


# Column Moving

This section describes how to make a column draggable and which type of movements you can do.

## Live example

{% hint style="success" %}
[Column Moving](https://demos.dikesoft.com/dk-grid/column/moving) live example.
{% endhint %}

## Allowing column dragging

To allow the user moves a column, you must set a flag at the grid scope by providing an input DikeGrid property named <mark style="color:orange;">`allowColumnDragging`</mark>. Its **default** value is **false**.

{% code title="column-moving.component.html" %}

```markup
<dike-grid id="grid-col-moving" height="650px" #grid="dkgGrid"
    [allowColumnDragging]="gridProperties.allowColumnDragging">
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can change the value of the <mark style="color:orange;">`allowColumnDragging`</mark> property at runtime.
{% endhint %}

You can open the [Floating Configuration Panel](/#floating-configuration-panel) of the live demo and change the value of the property <mark style="color:orange;">`allowColumnDragging`</mark>.

![Floating Configuration Panel - Dragging](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fv8NyyoupJ0lcSqptEayo%2Fdragging-panel-conf.png?alt=media\&token=982a2a66-6959-4cc4-8515-2cbcd9d9beca)

## Draggable Columns

You can make a column **movable** by providing a property named <mark style="color:orange;">`draggable`</mark>. Its **default** value is **false**.

{% hint style="warning" %}
Be aware that you can not change the <mark style="color:orange;">`draggable`</mark> property at runtime. Use the <mark style="color:orange;">`locked`</mark> property instead if you want the user not to move a column at runtime. For more details, see the Column Pinning section.
{% endhint %}

Let us set the following column configuration:

{% code title="column-moving.component.html" %}

```markup
<dike-grid id="grid-col-moving" height="650px" #grid="dkgGrid"
    allowRowGrouping
    allowColumnDragging>

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        order="2">
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150"
        minWidth="50"
        maxWidth="300"
        order="1"
        resizable
        draggable
        groupable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="0"
        resizable
        draggable>
        <dike-grid-column
            fieldName="completeNameGroup"
            headerText="Complete Name"
            draggable>

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>

            <dike-grid-column
                fieldName="lastName"
                headerText="Surname"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>
        </dike-grid-column>

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110"
            minWidth="50"
            maxWidth="200"
            resizable
            draggable
            groupable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85"
            minWidth="50"
            maxWidth="120"
            resizable
            draggable
            groupable>
        </dike-grid-column>
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        order="1"
        draggable
        groupable
        panel="rightPanel">
    </dike-grid-column>
</dike-grid>
```

{% endcode %}

With this configuration, we have:

1. All columns and column groups are **movable** except the **Name** column in the **root** group and the **Name** and **Surname** columns under the **Complete Name** group.
2. We have enabled the row grouping to move columns to the group panel. Columns **Surname** and **Hire Date** from the **root** group are **groupable**. **Gender** and **Age** columns under the **Personal Info** group are **groupable** as well.
3. All columns are in the center panel except the **Hire Date** column in the right panel.

{% hint style="warning" %}
You can move only **data** columns to the **group panel**, not column groups.

When you move a column group to the group panel using the **API**, the movement inserts only the **descendant data columns** into the group panel.
{% endhint %}

## Column movements

Once we have established some columns as **draggable**, be aware of the valid movements a column could take.

{% hint style="success" %}
For the description of the following movements, it will be helpful to grasp the [Gutters](/fundamentals/grid-structure/gutters) structure of the DikeGrid.
{% endhint %}

### Interchange Columns

This movement will swap two columns. The **source column** will take the **destination column's** place and vice versa. For this movement, the following rules apply:

1. The columns must belong to the same group of columns.
2. You can interchange columns that belong to the Content panels.

![Interchange Columns](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FyOZckxOd9Yca3iXtSwlQ%2Fcolumn-moving-interchange.gif?alt=media\&token=3e990ddb-b3de-4c1e-ae6f-907c56c41cc7)

{% hint style="info" %}
If you try to swap a ***broken group***, you will join the group in the destination panel.
{% endhint %}

### Move Column Before

You can move a column ***before*** another column.&#x20;

Let us say that you can move a given **column** at the left side of the **target column**. The actual movement will insert the given **column** at the **left-gutter** of the **target column**. Therefore, consider the following rules:

1. Columns must belong to the same group of columns.
2. The target column must belong to the right panel because columns have their gutter at their left.
3. If the target column lives in the left, center panel, or group panel, it must be the first column because they have both gutters.

![Move Column Before](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Ft77Q1WxMIWFaepkjEg7T%2Fcolumn-moving-before.gif?alt=media\&token=c6202027-2f93-47bb-bd2a-385d9d33bc7f)

### Move Column After

You can move one column ***after*** another column.

Same as before, let us say that you can move a given **column** at the right side of the **target column**. The actual movement will insert the given **column** at the **right-gutter** of the **target column**.&#x20;

The rules are:

1. Columns must belong to the same group of columns.
2. The target column must belong to the group, left or center panel because columns have their gutter at their right.
3. If the target column lives in the right panel, it must be the last column because it has both gutters.

![Move Column After](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FUbMI1bz6vghgLVdxmGyp%2Fcolumn-moving-after.gif?alt=media\&token=f1125acd-0ee2-4ca1-8fde-7066706d39c5)

### Column to panel&#x20;

This movement is possible when you drag a column to the ***left*** or ***right*** grid **border** or use the ***Pinning*** submenu of the **Column Context Menu**.

#### Drag a column to the grid's borders

Drag a column to the left or right grid border, as you can see in the following gif.

![Column to panel](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FgY5VFTMDoMXYJ95GLdzu%2Fcolumn-moving-column-to-panel.gif?alt=media\&token=b6a4b6ad-4c1c-4897-98e5-24a4aa02cda8)

#### Using the Column Context Menu

Now, click on any [Column Context Menu](/fundamentals/grid-structure/column-context-menu) and select a panel to move the column.

![Column to panel using the Column Context Menu](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FROhT5TqujX5X5ffoeoun%2Fcolumn-moving-column-to-panel-context-menu.gif?alt=media\&token=0738ce07-8221-4ce1-a953-372a09424373)

{% hint style="info" %}
It is possible to move **all panel columns** to another panel using the **column** **API**.
{% endhint %}

## Breaking a column group&#x20;

You can take a column that belongs to a column group and place it in another panel. This move grabs the column with its parent column and its parent, and so forth until the root.

The movement's outcome is a ***broken group***. For example, we moved the **Gender** column to the left panel. See the final state of the **Personal Info** group in the following screenshot.

![Breaking a column group](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F28ThfZmMehgD6f6xHac2%2Fcolumn-moving-broken-groups.png?alt=media\&token=cd1e6187-717d-4c9d-89e2-16733f9592b6)

### Unbroken column groups

If you do not want the user breaks a group, make its child columns **non-draggable**, as we did for **Name** and **Surname** columns under the **Complete Name** column group.

{% code title="column-moving.component.html" %}

```markup
<dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name"
        draggable>
        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            minWidth="50"
            maxWidth="300"
            resizable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            minWidth="50"
            maxWidth="300"
            resizable>
        </dike-grid-column>
</dike-grid-column>
```

{% endcode %}

You can not move the **Name** column or the **Surname** column. You can only move those columns by dragging the **Complete Name** group.

![Unbroken column groups](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FzlDMsIeprjaBRO9wAixr%2Fcolumn-moving-unbroken-groups.gif?alt=media\&token=2d5fb79f-3c42-4e42-b05d-405ae7fc7e29)

{% hint style="info" %}
When you drag a column, the DikeGrid instance **highlights** the proper columns or gutters to drop the column you are moving.
{% endhint %}

## Move columns using the API

You can achieve the previous movements using the column API.

{% hint style="info" %}
Remember, to use the **column API**, you must grab the instance of the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef#methods)service.
{% endhint %}

The following table shows the desired movement and its related method in the column API.

| Movement            | Method(s)                                                                                                                                            |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Interchange Columns | `swapColumns()`                                                                                                                                      |
| Move Column Before  | `moveColumnBefore()`                                                                                                                                 |
| Move Column After   | `moveColumnAfter()`                                                                                                                                  |
| Column to Panel     | <p><code>moveColumnIntoPanel()</code><br><br>To move all columns from one panel to another panel, use the method <code>movePanelColumns()</code></p> |

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

{% hint style="info" %}
Even though you do not set the flag <mark style="color:orange;">`allowColumnDragging`</mark>, you can move a column using the API, but the column must be set as **draggable** and not **locked**.
{% endhint %}

To see the use of the API, we have created the following UI:

{% code title="column-moving.component.html" %}

```markup
<form [formGroup]="moveColumnsForm"
    class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Source</mat-label>
        <mat-select formControlName="source" required
            (selectionChange)="onSourceSelectionChange($event)">
            <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of gridPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Destination</mat-label>
        <mat-select formControlName="destination" required
            (selectionChange)="onDestinationSelectionChange($event)">
            <mat-option *ngFor="let column of destinationColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of destinationPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-radio-group class="flex-none w-40 m-2 flex flex-col"
        #radioMethods="matRadioGroup" formControlName="method" required>
        <mat-radio-button class="my-2" value="swap-columns">swap-columns</mat-radio-button>
        <mat-radio-button class="my-2" value="column-after">column-after</mat-radio-button>
        <mat-radio-button class="my-2" value="column-before">column-before</mat-radio-button>
    </mat-radio-group>

    <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"
            [disabled]="radioMethods.disabled || !moveColumnsForm.valid"
            (click)="onMoveColumns()">Move column(s)
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!radioMethods.disabled"
            (click)="onMoveColumnsToPanel()">Move column(s) to Panel
        </button>
    </div>
</form>
```

{% endcode %}

We added the <mark style="color:red;">`form`</mark> selector above the <mark style="color:red;">`dike-grid`</mark> selector, generating the following output:

![Moving columns using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4VGYri3SxZQ7mVgT6xHt%2Fcolumn-moving-ui-for-api.png?alt=media\&token=1b6d7168-f4e5-445a-82e8-53b93aaa16d6)

With the previous UI, you can do the following tasks:

1. You can choose the source column or the source panel from the first select control.
2. Depending on your selected option, the second dropdown control will have a proper destination place. For instance, if you choose a column, the destination control will contain all columns of the same group and panels the column does not live.
3. The radio-button group has methods that involve column between column movements.
4. When you select panels, the corresponding button is enabled.

It is essential to notice that we are **listening** to any change in the **DikeGrid's columns**, attaching a subscription to the `columnsChange` <mark style="color:green;">`Observable`</mark> from the <mark style="color:green;">`DikeGridColumnDef`</mark>.

{% code title="column-moving.component.ts" %}

```typescript
private setChangeGridColumnsSubscription(): void {
  this.changeGridColumnsSubscription.unsubscribe();
  this.changeGridColumnsSubscription = this.dikeGrid.columnDef.columnsChange.pipe(
    map((columns: DikeColumnDef[]) => this.flatColumns(columns)),
    observeOn(asapScheduler)

  ).subscribe((columns) => {
    this.destinationColumns = null;
    this.destinationPanels = null;
    this.moveColumnsForm.patchValue({ source: null, destination: null, method: '' });
    this.moveColumnsForm.get('method').enable();
    this.gridColumns = columns;
  });
}
```

{% endcode %}

Every time we receive a new set of columns, we flat them into a single array, invoking the method `flatColumns()`. So indeed, we are **flattening** the **column groups**, traversing them **recursively**.

{% code title="column-moving.component.ts" %}

```typescript
private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
  return columns.reduce((accum, column) => {
    if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
      return [ ...accum, column, ...this.flatColumns(column.children) ];
    }

    return [ ...accum, column ];
  }, [ ]);
}
```

{% endcode %}

If you see the content of the **source dropdown** control, you see the columns **Hire Date**, **Employee Id**, **Personal Info** group, **Complete Name** group, then **Name** and **Surname** columns, **Gender** and **Age** columns, etc.

![Flattening column groups](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fdk5enRB6YBZoLRYSejkn%2Fcolumn-moving-flattening-groups.png?alt=media\&token=b56d96a6-e508-40ad-a368-ae31eabd0c0c)

{% hint style="info" %}
The **non-draggable** columns are **disabled**, such as **Name** and **Surname**, under the **Complete Name** group.
{% endhint %}

## Column Moving Events

Every previous movement emits an event of type <mark style="color:green;">`DikeColumnMoveEvent`</mark>. We listen to the `columnMove` event at the grid scope.

{% tabs %}
{% tab title="column-moving.component.html" %}

```markup
<dike-grid id="grid-col-moving" height="650px" #grid="dkgGrid"
    (columnMove)="onColumnMove($event)">
</dike-grid>
```

{% endtab %}

{% tab title="column-moving.component.ts" %}

```typescript
onColumnMove(movementData: DikeColumnMoveEvent): void {
    console.log('Column movement: ', movementData);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
You can also listen to the `columnMove` event from the [<mark style="color:green;">`DikeGridColumnDef<T>`</mark>](/reference/dkgrid-api/dkgridcolumndef#events) instance.
{% endhint %}

See the definition of the <mark style="color:green;">`DikeColumnMoveEvent`</mark> interface:

```typescript
interface DikeColumnMoveEvent {
  type: ColumnMovement;
  movedColumns: DikeColumnDef[];
  beforeColumn?: DikeColumnDef;
  afterColumn?: DikeColumnDef;
  destinationPanel?: DikeGridPanel;
}
```

And the <mark style="color:green;">`ColumnMovement`</mark> definition is:

```typescript
type ColumnMovement = 'swap-columns' | 'join-column-groups' | 'column-after' | 'column-before' | 'columns-to-panel';
```

As you can see, some fields are not required. However, depending on the type of movement, those fields are provided.

| Movement            | DikeColumnMoved                                                                                                                                                                                                                                                 |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Interchange Columns | <p><strong>type</strong>: <mark style="color:red;">swap-columns</mark>.<br><strong>movedColumns</strong>: The source and destination columns.</p>                                                                                                               |
| Interchange Columns | <p><strong>type</strong>: <mark style="color:red;">join-column-groups</mark>.<br><strong>movedColumns</strong>: The joined column group.<br><strong>destinationPanel</strong>: The panel the broken-destination group lives.</p>                                |
| Move Column Before  | <p><strong>type</strong>: <mark style="color:red;">column-before</mark>.<br><strong>movedColumns</strong>: The source column.<br><strong>beforeColumn</strong>: The target column.<br><strong>destinationPanel</strong>: The panel the target column lives.</p> |
| Move Column After   | <p><strong>type</strong>: <mark style="color:red;">column-after</mark>.<br><strong>movedColumns</strong>: The source column.<br><strong>afterColumn</strong>: The target column.<br><strong>destinationPanel</strong>: The panel the target column lives.</p>   |
| Column to panel     | <p><strong>type</strong>: <mark style="color:red;">columns-to-panel</mark>.<br><strong>movedColumns</strong>: The source columns or all panel columns.<br><strong>destinationPanel</strong>. The provided panel is the destination panel.</p>                   |

{% hint style="success" %}
You can open the **dev console** for the live demo and see the emitted **event**.
{% endhint %}

## Summary

Before defining which columns will be movable, you must allow this action by providing a property named <mark style="color:orange;">`allowColumnDragging`</mark> at the grid scope. To move a column, you must define a column as **draggable**. You can **interchange** columns only from the **Content panels**, and you can only add **data columns** to the **group panel**. You can move columns using the [Column Context Menu](/fundamentals/grid-structure/column-context-menu) as well.

### Complete code for this section

{% tabs %}
{% tab title="column-moving.component.html" %}

```markup
<form [formGroup]="moveColumnsForm"
    class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Source</mat-label>
        <mat-select formControlName="source" required
            (selectionChange)="onSourceSelectionChange($event)">
            <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of gridPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Destination</mat-label>
        <mat-select formControlName="destination" required
            (selectionChange)="onDestinationSelectionChange($event)">
            <mat-option *ngFor="let column of destinationColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of destinationPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-radio-group class="flex-none w-40 m-2 flex flex-col"
        #radioMethods="matRadioGroup" formControlName="method" required>
        <mat-radio-button class="my-2" value="swap-columns">swap-columns</mat-radio-button>
        <mat-radio-button class="my-2" value="column-after">column-after</mat-radio-button>
        <mat-radio-button class="my-2" value="column-before">column-before</mat-radio-button>
    </mat-radio-group>

    <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"
            [disabled]="radioMethods.disabled || !moveColumnsForm.valid"
            (click)="onMoveColumns()">Move column(s)
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!radioMethods.disabled"
            (click)="onMoveColumnsToPanel()">Move column(s) to Panel
        </button>
    </div>
</form>

<dike-grid id="grid-col-moving" height="650px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (columnMove)="onColumnMove($event)"

    [allowColumnDragging]="gridProperties.allowColumnDragging"
    allowRowGrouping

    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        order="3">
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150"
        minWidth="50"
        maxWidth="300"
        order="2"
        resizable
        draggable
        groupable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="1"
        resizable
        draggable>
        <dike-grid-column
            fieldName="completeNameGroup"
            headerText="Complete Name"
            draggable>
            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>

            <dike-grid-column
                fieldName="lastName"
                headerText="Surname"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>
        </dike-grid-column>

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110"
            minWidth="50"
            maxWidth="200"
            resizable
            draggable
            groupable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85"
            minWidth="50"
            maxWidth="120"
            resizable
            draggable
            groupable>
        </dike-grid-column>
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        width="125"
        order="4"
        draggable
        groupable
        panel="rightPanel">
    </dike-grid-column>
</dike-grid>
```

{% endtab %}

{% tab title="column-moving.component.ts" %}

```typescript
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatSelectChange } from '@angular/material/select';

import { asapScheduler, Subscription } from 'rxjs';
import { map, observeOn } from 'rxjs/operators';

import { DikeColumnDef, DikeGridComponent, DikeGridDataSourceInput, isDikeGroupColumnDef, DikeGridPanel, DikeColumnMoveEvent } from '@dikesoft/angular-data-grid';
import { Employee } from 'app/mock-api/common/employees/data.model';
import { DikeGridProperties } from 'app/core/config/dike-grid.properties';

import { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  gridColumns: DikeColumnDef[];
  gridPanels: DikeGridPanel[];
  destinationColumns: DikeColumnDef[];
  destinationPanels: DikeGridPanel[];

  disableMethods: boolean;
  moveColumnsForm: FormGroup;

  private changeGridColumnsSubscription: Subscription = Subscription.EMPTY;
  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

    this.gridPanels = ['groupPanel', 'leftPanel', 'centerPanel', 'rightPanel'];
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
    this.createForm();
  }

  ngAfterViewInit(): void {
    // We listen the columnsChange observable after the DikeGrid instance is ready:
    this.setChangeGridColumnsSubscription();
  }

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

  onSourceSelectionChange(data: MatSelectChange): void {
    const source = data.value;
    let sourcePanel: DikeGridPanel;

    this.destinationColumns = null;
    this.moveColumnsForm.patchValue({ destination: null, method: '' });
    this.moveColumnsForm.get('method').enable();

    if (source instanceof DikeColumnDef) {
      // We get only those columns in the same group:
      this.destinationColumns = this.gridColumns.filter(column => column !== source &&
        column.belongToGroup === source.belongToGroup);

      sourcePanel = source.panel;

    } else {
      sourcePanel = source as DikeGridPanel;
    }

    // We take the panels different from the source selection:
    this.destinationPanels = this.gridPanels.filter(panel => sourcePanel !== panel);
  }

  onDestinationSelectionChange(data: MatSelectChange): void {
    const destination = data.value;

    if (!(destination instanceof DikeColumnDef)) {
      this.moveColumnsForm.patchValue({ method: '' });
      this.moveColumnsForm.get('method').disable();

    } else {
      this.moveColumnsForm.get('method').enable();
    }
  }

  onMoveColumns(): void {
    if (this.moveColumnsForm.valid) {
      // Take the columns from the form:
      const sourceColumn = this.moveColumnsForm.get('source').value as DikeColumnDef;
      const destinationColumn = this.moveColumnsForm.get('destination').value as DikeColumnDef;
      // Take the method to be invoked:
      const method = this.moveColumnsForm.get('method').value as string;

      switch (method) {
        case 'swap-columns':
          this.dikeGrid.columnDef.swapColumns(sourceColumn, destinationColumn);
          break;

        case 'column-after':
          this.dikeGrid.columnDef.moveColumnAfter(sourceColumn, destinationColumn);
          break;

        case 'column-before':
          this.dikeGrid.columnDef.moveColumnBefore(sourceColumn, destinationColumn);
          break;
      }
    }
  }

  onMoveColumnsToPanel(): void {
    const source = this.moveColumnsForm.get('source').value;
    const destinationPanel = this.moveColumnsForm.get('destination').value as DikeGridPanel;

    if (!!source && !!destinationPanel) {
      if (source instanceof DikeColumnDef) {
        // Move the selected column to the destination panel:
        this.dikeGrid.columnDef.moveColumnIntoPanel(source, destinationPanel);

      } else {
        // Move all columns from the source panel to the destnation panel:
        this.dikeGrid.columnDef.movePanelColumns(source as DikeGridPanel, destinationPanel);
      }
    }
  }

  onColumnMove(movementData: DikeColumnMoveEvent): void {
    console.log('Column movement: ', movementData);
  }

  private createForm(): void {
    this.moveColumnsForm = new FormGroup({
      source: new FormControl(null, Validators.required),
      destination: new FormControl(null, Validators.required),
      method: new FormControl({ value: '', disabled: false }, Validators.required),
    });
  }

  private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
    return columns.reduce((accum, column) => {
      if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
        return [ ...accum, column, ...this.flatColumns(column.children) ];
      }

      return [ ...accum, column ];
    }, [ ]);
  }

  private setChangeGridColumnsSubscription(): void {
    this.changeGridColumnsSubscription.unsubscribe();
    this.changeGridColumnsSubscription = this.dikeGrid.columnDef.columnsChange.pipe(
      map((columns: DikeColumnDef[]) => this.flatColumns(columns)),
      observeOn(asapScheduler)

    ).subscribe((columns) => {
      this.destinationColumns = null;
      this.destinationPanels = null;
      this.moveColumnsForm.patchValue({ source: null, destination: null, method: '' });
      this.moveColumnsForm.get('method').enable();
      this.gridColumns = columns;
    });
  }

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

```

{% endtab %}
{% endtabs %}


# Column Pinning

Once you have defined a column as draggable, you can make it non-draggable at runtime.

## Live example

{% hint style="success" %}
[Column Pinning](https://demos.dikesoft.com/dk-grid/column/pinning) live example.
{% endhint %}

## Lock/Unlock columns

When you define a column as **draggable**, it is possible to **freeze** that column by locking it.

{% hint style="info" %}
To **lock/unlock** a column through the **UI**, you must set the flag <mark style="color:orange;">`allowColumnDragging`</mark> at the grid scope.
{% endhint %}

Open the [Column Context Menu](/fundamentals/grid-structure/column-context-menu#column-pinning) for any column defined as **draggable**. You will see the **lock/unlock** icons.

![Lock/Unlock icons](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FANVmmYjfjPwhdjBW2r3F%2Fcolumn-pinning-lock-unlock-icons.png?alt=media\&token=6db9f1ee-c87f-45b2-ba3d-bb7b4c347baa)

Locking a column is useful when the user moves a column to the **right** or **left** panel and then **freezes** the column to be always **visible**. Even though the user can lock a column in the center panel, it makes more sense to freeze columns in the left or right panels.

{% hint style="info" %}
The user can not move locked columns, and other columns can not occupy their place when dragging operation occurs.
{% endhint %}

## Defining frozen columns

To not allow a user to move a column nor lock/unlock it, define the column as **non-draggable**.

{% hint style="success" %}
Only **draggable** columns are susceptible to being **locked/unlocked**.
{% endhint %}

Consider the following column configuration:

{% code title="column-pinning.component.html" %}

```markup
<dike-grid id="grid-col-pinning" height="650px" #grid="dkgGrid"
    [allowColumnDragging]="gridProperties.allowColumnDragging">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150">
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        draggable
        groupable>
    </dike-grid-column>

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

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        draggable>

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

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                draggable
                locked>
            </dike-grid-column>

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

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="130"
            draggable
            groupable>
        </dike-grid-column>

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

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        width="150"
        draggable
        groupable>
    </dike-grid-column>
</dike-grid>
```

{% endcode %}

With this configuration, we have:

1. All columns are **draggable** except the **Employee Id** column, the **Name** column in the **root** group, and the **Surname** under the **Complete Name** column group.
2. We **locked** the columns **Email** and **Name** under the **Complete Name** group.
3. Finally, we defined the **Employee Id** column in the **left panel**, which is **non-draggable**. The user can not move or lock/unlock this column.

As you can see, the user can not move the Employee Id column and will not lock/unlock it. If you try to move the column, you will see an icon that indicates the user cannot move that column.

![Defining frozen columns](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F9oJ3D64CUChzvwRpjsMv%2Fcolumn-pinning-frozen-column.gif?alt=media\&token=6910f1a6-d0fe-41ef-9563-07b90ba26964)

## Lock/Unlock column using the API

You can lock/unlock columns using the column API.

{% hint style="info" %}
Remember, to use the **column API**, you must grab the instance of the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef#methods)service.
{% endhint %}

You use `lockColumn()` and `unlockColumn()` methods for locking and unlocking, respectively.

{% hint style="success" %}
Although you do not set the flag <mark style="color:orange;">`allowColumnDragging`</mark>, you can **lock/unlock** a column using the **API**, but you must define the column as **draggable**.
{% endhint %}

To see the use of the API, we have created the following API:

{% code title="column-pinning.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Columns</mat-label>
        <mat-select [formControl]="selectedColumn" required
            (selectionChange)="onSelectionChange($event)">
            <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!column.draggable">
                {{ column.headerText }}
            </mat-option>
        </mat-select>
    </mat-form-field>
    
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        [disabled]="!selectedColumn.valid"
        (click)="onChangeLocking()">{{ isColumnLocked ? 'unlock': 'lock' }} column
    </button>
</div>
```

{% endcode %}

The generated output is:

![Lock/Unlock columns using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FTP7WONBjgVZAb7iKcLiJ%2Fcolumn-moving-ui-for-using-api.png?alt=media\&token=bbb9cfbf-4638-4718-b880-5673a6931707)

With the previous UI, you can do the following tasks:

1. You can choose any defined column as **draggable**. It includes column groups.
2. Depending on the locked state, the button next to the dropdown control will display the lock or unlock label.

It is essential to notice that we are **listening** to any change in the **DikeGrid's columns**, attaching a callback to the `columnsChange` output property from the [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent).

{% tabs %}
{% tab title="column-pinning.component.html" %}

```markup
<dike-grid id="grid-col-pinning" height="650px" #grid="dkgGrid"
    (columnsChange)="onColumnsChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="column-pinning.component.ts" %}

```typescript
onColumnsChange(columns: DikeColumnDef[]): void {
    this.gridColumns = this.flatColumns(columns);
    // Clear selection:
    this.selectedColumn.patchValue(null);
}
```

{% endtab %}
{% endtabs %}

Every time we receive a new set of columns, we flat them into a single array, invoking the method `flatColumns()`. So indeed, we are **flattening** the **column groups**, traversing them **recursively**.

{% code title="column-pinning.component.ts" %}

```typescript
private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
  return columns.reduce((accum, column) => {
    if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
      return [ ...accum, column, ...this.flatColumns(column.children) ];
    }

    return [ ...accum, column ];
  }, [ ]);
}
```

{% endcode %}

If you see the content of the **dropdown** control, you see the columns **Employee Id**, **Name**, **Personal Info** group, **Complete Name** group, then **Name** and **Surname** columns, etc.

![Flattening column groups](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FnvzxC3nBPxwGYfI4hVfv%2Fcolumn-pinning-flat-columns.png?alt=media\&token=ca8c0f97-6539-413f-9a3d-550e3f1a7f12)

{% hint style="info" %}
The **non-draggable** columns are **disabled**, such as **Employee Id**, **Name** in the **root** group, and **Surname**, under the **Complete Name** group.
{% endhint %}

## Events when Locking/Unlocking columns

When you lock or unlock a column, the DikeGrid instance emits the related event.

{% tabs %}
{% tab title="column-pinning.component.html" %}

```markup
<dike-grid id="grid-col-pinning" height="650px" #grid="dkgGrid"
    (columnLockedChange)="onColumnLockedChange($event)"
    (columnUnLockedChange)="onColumnUnLockedChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="column-pinning.component.ts" %}

```typescript
onColumnLockedChange(column: DikeColumnDef): void {
  console.log('Column locked: ', column);
}

onColumnUnLockedChange(column: DikeColumnDef): void {
  console.log('Column unlocked: ', column);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
You can also listen to the `columnLockedChange` and `columnUnLockedChange` events from the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef#events)instance.
{% endhint %}

Both events emit the locked/unlocked column. Notice that the sent column is of type <mark style="color:green;">`DikeColumnDef`</mark> because you can lock/unlock column **groups**, not only data columns.

{% hint style="success" %}
Open the **dev console** to see the locked/unlocked column.
{% endhint %}

## Summary

To lock/unlock a column, you must define it as **draggable**. You can lock/unlock columns in any panel, even in the **group** panel, but **freezing** columns in the **left** or **right** panels makes more sense.

### Complete code for this section

{% tabs %}
{% tab title="column-pinning.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Columns</mat-label>
        <mat-select [formControl]="selectedColumn" required
            (selectionChange)="onSelectionChange($event)">
            <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!column.draggable">
                {{ column.headerText }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        [disabled]="!selectedColumn.valid"
        (click)="onChangeLocking()">{{ isColumnLocked ? 'unlock': 'lock' }} column
    </button>
</div>

<dike-grid id="grid-col-pinning" height="650px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (columnsChange)="onColumnsChange($event)"
    (columnLockedChange)="onColumnLockedChange($event)"
    (columnUnLockedChange)="onColumnUnLockedChange($event)"

    [allowColumnDragging]="gridProperties.allowColumnDragging"
    allowRowGrouping
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150">
    </dike-grid-column>

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        draggable
        groupable>
    </dike-grid-column>

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

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        draggable>

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

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                draggable
                locked>
            </dike-grid-column>
    
            <dike-grid-column
                fieldName="lastName"
                headerText="Surname"
                dataType="Text"
                width="150">
            </dike-grid-column>
        </dike-grid-column>
    
        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="130"
            draggable
            groupable>
        </dike-grid-column>

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

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        width="150"
        draggable
        groupable>
    </dike-grid-column>
</dike-grid>
```

{% endtab %}

{% tab title="column-pinning.component.ts" %}

```typescript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { MatSelectChange } from '@angular/material/select';

import { Subscription } from 'rxjs';

import { DikeGridDataSourceInput, DikeColumnDef, isDikeGroupColumnDef, DikeGridComponent } 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 { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  selectedColumn: FormControl;
  isColumnLocked: boolean;
  gridColumns: DikeColumnDef[];

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

    this.isColumnLocked = false;
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();

    this.selectedColumn = new FormControl(null, Validators.required);
  }

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

  onSelectionChange(data: MatSelectChange): void {
    this.isColumnLocked = (data.value as DikeColumnDef).locked;
  }

  onChangeLocking(): void {
    if (this.selectedColumn.valid) {
      const column = this.selectedColumn.value as DikeColumnDef;

      if (column.locked) {
        this.dikeGrid.columnDef.unlockColumn(column);

      } else {
        this.dikeGrid.columnDef.lockColumn(column);
      }
    }
  }

  onColumnsChange(columns: DikeColumnDef[]): void {
    this.gridColumns = this.flatColumns(columns);
    // Clear selection:
    this.selectedColumn.patchValue(null);
  }

  onColumnLockedChange(column: DikeColumnDef): void {
    console.log('Column locked: ', column);
  }

  onColumnUnLockedChange(column: DikeColumnDef): void {
    console.log('Column unlocked: ', column);
  }

  private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
    return columns.reduce((accum, column) => {
      if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
        return [ ...accum, column, ...this.flatColumns(column.children) ];
      }

      return [ ...accum, column ];
    }, [ ]);
  }

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

```

{% endtab %}
{% endtabs %}


# Row Sorting

This section describes how you can sort the DikeGrid rows.

## Live example

{% hint style="success" %}
[Row Sorting](https://demos.dikesoft.com/dk-grid/row/sorting) live example.
{% endhint %}

## Allowing the sorting operation

To allow the user to sort the DikeGrid rows, you must set a flag at the grid scope by providing an input property named <mark style="color:orange;">`allowSorting`</mark>. Its **default** value is **false**.

{% code title="row-sorting.component.html" %}

```markup
<dike-grid id="grid-row-sorting" height="650px" #grid="dkgGrid"
    [allowSorting]="gridProperties.allowSorting">
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can change the value of the <mark style="color:orange;">`allowSorting`</mark> property at **runtime**.
{% endhint %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) for the live example and **mark** the **Allow Sorting** checkbox.

![Floating Configuration Panel - Sorting](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FiLAwaKAtLVpzqnVPqLnQ%2Fsorting-panel-conf.png?alt=media\&token=2842ef4c-7dac-42d3-b571-6056e4b6be62)

## Sortable columns

If you want the user to sort the DikeGrid rows by a column, mark the column as **sortable**. Its default value is **false**.

{% hint style="info" %}
You can only make **sortable** data columns, not column groups.
{% endhint %}

See the following column configuration:

{% code title="row-sorting.component.html" %}

```markup
<dike-grid id="grid-row-sorting" height="650px" #grid="dkgGrid"
    [allowSorting]="gridProperties.allowSorting">
    
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350">
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info">

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

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                sortable>
            </dike-grid-column>

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

        </dike-grid-column>

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

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

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

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

{% endcode %}

We have the following **sortable** columns with the previous configuration: **Name** and **Surname** under the **Complete Name** group, **Age** column under the **Personal Info** group, and the **Hire Date** column.

You can make a column **sortable** at **runtime** using the corresponding **API**. For example, see the following code snippet:

{% code title="row-sorting.component.ts" %}

```typescript
onSetColumnSortable(): void {
  if (this.columnControl.valid) {
    // Get the selected column:
    const column = this.columnControl.value as DikeColumnDef;

    if (isDikeDataColumnDef(column)) {
      // Toggle the sortable flag for the selected column:
      this.dikeGrid.columnDef.setColumnSortable(column, !column.sortable);
    }
  }
}
```

{% endcode %}

As you can see, once you have got the column you want to set as **sortable** or **non-sortable**, use the <mark style="color:green;">`DikeGridColumnDef`</mark> service instance to change the column state.

{% hint style="success" %}
Before using the **column API**, you must grab the instance of the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef) service.
{% endhint %}

## Sorting rows through the UI

Once you define columns as **sortable**, you can sort the DikeGrid rows by these sortable columns.

### Clicking on the column header

You can sort the DikeGrid rows by clicking on the column header.

When you have clicked on the column header of a **sortable** column, you will see an **arrow** that indicates the **direction** of the sorting operation.

![Sorting by Name column clicking on the column header](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fn0V2XXpeSoopQ0hwCTcd%2Frow-sorting-column-header.gif?alt=media\&token=42403f5a-821d-47e8-971c-66135c506b40)

### Column Context Menu

When you define a column as **sortable**, you will see the sorting submenu in the [Column Context Menu](/fundamentals/grid-structure/column-context-menu). Then, you will see the sorting options.

![Sorting By Name column using the Column Context Menu](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FfT6OPt2E3O95g9k1jUww%2Frow-sorting-context-menu.gif?alt=media\&token=9add6add-fd97-48d8-bd43-d7ac67ca69ec)

## DikeGrid Sorting API

You can sort the DikeGrid rows using the **sorting API**.

Before using the **sorting API**, you must retrieve the corresponding DikeGrid instance by querying the component's view.

{% tabs %}
{% tab title="row-sorting.component.html" %}

```markup
<dike-grid id="grid-row-sorting" height="650px" #grid="dkgGrid">
</dike-grid>
```

{% endtab %}

{% tab title="row-sorting.component.ts" %}

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

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

{% endtab %}
{% endtabs %}

{% hint style="success" %}
The **sorting API** is an instance of type [<mark style="color:green;">`DikeGridSorting`</mark>](/reference/dkgrid-api/dkgridsorting) under the [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent) through the **read-only** property named <mark style="color:orange;">`sorting`</mark>.
{% endhint %}

The **sorting API** has the following methods:

| Method                 | Description                                                                                   |
| ---------------------- | --------------------------------------------------------------------------------------------- |
| `sortBy()`             | It sorts the DikeGrid rows by the given column in the given direction.                        |
| `isDikeGridSortedBy()` | It evaluates if the DikeGrid is sorted by the given column.                                   |
| `getCurrentSortable()` | It returns the column and the direction of the sorting operation. Otherwise, it returns null. |
| `clearSorting()`       | It removes the current sorting operation.                                                     |

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

{% hint style="info" %}
You can sort the DikeGrid rows only by **one** column **sortable** at a time.
{% endhint %}

To see the use of the **sorting API**, we have created the following UI:

{% tabs %}
{% tab title="row-sorting.component.html" %}

```markup
<form [formGroup]="rowSortingForm"
  class="mt-2 flex flex-row flex-wrap items-center justify-around">
  <mat-form-field appearance="fill" class="flex-none w-56 m-2">
    <mat-label>Columns</mat-label>
      <mat-select [formControl]="columnControl" (selectionChange)="onSelectionChange($event)" required>
          <mat-select-trigger>
            <div class="w-40 flex flex-row items-center justify-between">
                <div>{{ columnControl.value?.headerText }}</div>
                <mat-icon *ngIf="columnControl.value && columnControl.value.sortable">sort_by_alpha</mat-icon>
            </div>
          </mat-select-trigger>
          <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!!column.children">
              <div class="flex flex-row items-center justify-between">
                <div>{{ column.headerText }}</div>
                <mat-icon *ngIf="column.sortable">sort_by_alpha</mat-icon>
              </div>
          </mat-option>
      </mat-select>
    </mat-form-field>

  <mat-radio-group class="flex-none w-56 m-2 flex flex-row justify-around"
      #radioDirections="matRadioGroup" formControlName="direction" required>
      <mat-radio-button class="my-2" value="asc">asc</mat-radio-button>
      <mat-radio-button class="my-2" value="desc">desc</mat-radio-button>
  </mat-radio-group>

  <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"
          [disabled]="!rowSortingForm.valid || radioDirections.disabled"
          (click)="onSortBy()">Sort by
      </button>

      <button mat-raised-button
          class="flex-none w-56 my-2"
          color="primary"
          [disabled]="!columnControl.valid"
          (click)="onSetColumnSortable()">Set column {{ columnControl.value?.sortable ? 'non-' : '' }}sortable
      </button>
  </div>
</form>

<dike-grid id="grid-row-sorting" height="650px" #grid="dkgGrid">
</dike-grid>

```

{% endtab %}

{% tab title="row-sorting.component.ts" %}

```typescript
onSelectionChange(data: MatSelectChange): void {
  const column = data.value as DikeColumnDef;

  if (isDikeDataColumnDef(column)) {
    if (!column.sortable) {
      this.rowSortingForm.get('direction').disable();

    } else {
      this.rowSortingForm.get('direction').enable();
    }
  }
}

onSortBy(): void {
  if (this.rowSortingForm.valid) {
    const column = this.rowSortingForm.get('column').value as DikeColumnDef;
    const direction = this.rowSortingForm.get('direction').value as 'asc' | 'desc';

    // if the DikeGrid instance is sorted, get the current columns and the sort direction:
    const currentSortable = this.dikeGrid.sorting.getCurrentSortable();

    /**
     * We will sort the DikeGrid rows, if only if:
     *
     *    1. The DikeGrid is not sorted.
     *    2. Or, the DikeGrid is sorted but the selected column is different than the current column.
     *    3. Or, the DikeGrid is sorted by the selected column but the selected direction is different.
     */
    if (isDikeDataColumnDef(column) &&
      (!currentSortable || currentSortable.sortedColumn.columnId !== column.columnId || currentSortable.type !== direction)) {
      this.dikeGrid.sorting.sortBy(column, direction);
    }
  }
}

onSetColumnSortable(): void {
  if (this.columnControl.valid) {
    // Get the selected column:
    const column = this.columnControl.value as DikeColumnDef;

    if (isDikeDataColumnDef(column)) {
      // Toggle the sortable flag for the selected column:
      this.dikeGrid.columnDef.setColumnSortable(column, !column.sortable);
    }
  }
}

```

{% endtab %}
{% endtabs %}

The previous HTML code generates the following output:

![Row sorting using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FR41TB87TR8SK1E3Hdjfy%2Frow-sorting-ui-for-api.png?alt=media\&token=e256d47f-0355-4733-9c10-fbe1cc951fd2)

With the previous UI, you can do the following tasks:

1. You can choose any data column from the dropdown control. Sortable columns have an icon indicating they are sortable.
2. You can change the sortable state of a data column.
3. To sort the DikeGrid rows, you must select the direction of the sorting operation. Then click on the **Sort By** button.

It is essential to notice that we are **listening** to any change in the **DikeGrid's columns**, attaching a callback to the `columnsChange` output property from the [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent).

{% tabs %}
{% tab title="row-sorting.component.html" %}

```markup
<dike-grid id="grid-row-sorting" height="650px" #grid="dkgGrid"
    (columnsChange)="onColumnsChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-sorting.component.ts" %}

```typescript
onColumnsChange(columns: DikeColumnDef[]): void {
    this.gridColumns = this.flatColumns(columns);
    // Clear selection:
    this.rowSortingForm.patchValue({ column: null, direction: '' });
}
```

{% endtab %}
{% endtabs %}

Every time we receive a new set of columns, we flat them into a single array, invoking the method `flatColumns()`. So indeed, we are **flattening** the **column groups**, traversing them **recursively**.

{% code title="row-sorting.component.ts" %}

```typescript
private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
  return columns.reduce((accum, column) => {
    if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
      return [ ...accum, column, ...this.flatColumns(column.children) ];
    }

    return [ ...accum, column ];
  }, [ ]);
}
```

{% endcode %}

If you see the content of the **dropdown** control, you see the columns **Employee Id**, **Personal Info** group, **Complete Name** group, then **Name** and **Surname** columns, etc.

![Flattening column groups](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FfUsQ68waYlfQFsPSrfx3%2Frow-sorting-flattening-groups.png?alt=media\&token=5d28022e-d685-42ee-867f-d34b60dd6dbf)

The column groups are **disabled**, such as **Personal Info** and **Complete Name**.

## Sort event

Every time we sort the DikeGrid rows, the DikeGrid instance emits the related event.

{% tabs %}
{% tab title="row-sorting.component.html" %}

```markup
<dike-grid id="grid-row-sorting" height="650px" #grid="dkgGrid"
    (sortChange)="onSortChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-sorting.component.ts" %}

```typescript
onSortChange(sortedBy: DikeColumnSortEvent<Employee>): void {
  console.log('Sort change: ', sortedBy);
}
```

{% endtab %}
{% endtabs %}

The emitted object is of type <mark style="color:green;">`DikeColumnSortEvent`</mark>, which contains the **column** and the **direction** of the current sorting operation.

{% hint style="success" %}
Open the **dev console** to see the output of the `sortChange` event.
{% endhint %}

{% hint style="info" %}
You can also listen to the `sortChange` event from the [<mark style="color:green;">`DikeGridSorting`</mark>](/reference/dkgrid-api/dkgridsorting#events) instance.
{% endhint %}

## Summary

To perform the sorting operation, you must allow it by providing an input property named <mark style="color:orange;">`allowSorting`</mark>. Then, when creating columns, you can define them as **sortable**. Nevertheless, you can change their state at **runtime** using the related **API**. You can perform the sorting operation through the **UI** or the **sorting API**.

### Complete code for this section

{% tabs %}
{% tab title="row-sorting.component.html" %}

```markup
<form [formGroup]="rowSortingForm"
  class="mt-2 flex flex-row flex-wrap items-center justify-around">
  <mat-form-field appearance="fill" class="flex-none w-56 m-2">
    <mat-label>Columns</mat-label>
      <mat-select [formControl]="columnControl" (selectionChange)="onSelectionChange($event)" required>
          <mat-select-trigger>
            <div class="w-40 flex flex-row items-center justify-between">
                <div>{{ columnControl.value?.headerText }}</div>
                <mat-icon *ngIf="columnControl.value && columnControl.value.sortable">sort_by_alpha</mat-icon>
            </div>
          </mat-select-trigger>
          <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!!column.children">
              <div class="flex flex-row items-center justify-between">
                <div>{{ column.headerText }}</div>
                <mat-icon *ngIf="column.sortable">sort_by_alpha</mat-icon>
              </div>
          </mat-option>
      </mat-select>
    </mat-form-field>
    
      <mat-radio-group class="flex-none w-56 m-2 flex flex-row justify-around"
          #radioDirections="matRadioGroup" formControlName="direction" required>
          <mat-radio-button class="my-2" value="asc">asc</mat-radio-button>
          <mat-radio-button class="my-2" value="desc">desc</mat-radio-button>
      </mat-radio-group>
    
      <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"
              [disabled]="!rowSortingForm.valid || radioDirections.disabled"
              (click)="onSortBy()">Sort by
          </button>

          <button mat-raised-button
              class="flex-none w-56 my-2"
              color="primary"
              [disabled]="!columnControl.valid"
              (click)="onSetColumnSortable()">Set column {{ columnControl.value?.sortable ? 'non-' : '' }}sortable
          </button>
      </div>
</form>

<dike-grid id="grid-row-sorting" height="650px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (columnsChange)="onColumnsChange($event)"
    (sortChange)="onSortChange($event)"

    [allowSorting]="gridProperties.allowSorting"
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info">

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

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                sortable>
            </dike-grid-column>

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

        </dike-grid-column>

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

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

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

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        width="150"
        sortable>
    </dike-grid-column>
</dike-grid>

```

{% endtab %}

{% tab title="row-sorting.component.ts" %}

```typescript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatSelectChange } from '@angular/material/select';

import { Subscription } from 'rxjs';

import { DikeColumnDef, DikeColumnSortEvent, DikeGridComponent, 
  DikeGridDataSourceInput, isDikeDataColumnDef, isDikeGroupColumnDef 
} 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 { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  gridColumns: DikeColumnDef[];
  rowSortingForm: FormGroup;
  columnControl: FormControl;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
    this.createForm();
  }
  
  ngOnDestroy(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
  }

  onSelectionChange(data: MatSelectChange): void {
    const column = data.value as DikeColumnDef;

    if (isDikeDataColumnDef(column)) {
      if (!column.sortable) {
        this.rowSortingForm.get('direction').disable();

      } else {
        this.rowSortingForm.get('direction').enable();
      }
    }
  }

  onSortBy(): void {
    if (this.rowSortingForm.valid) {
      const column = this.rowSortingForm.get('column').value as DikeColumnDef;
      const direction = this.rowSortingForm.get('direction').value as 'asc' | 'desc';

      // if the DikeGrid instance is sorted, get the current columns and the sort direction:
      const currentSortable = this.dikeGrid.sorting.getCurrentSortable();

      /**
       * We will sort the DikeGrid rows, if only if:
       *
       *    1. The DikeGrid is not sorted.
       *    2. Or, the DikeGrid is sorted but the selected column is different than the current column.
       *    3. Or, the DikeGrid is sorted by the selected column but the selected direction is different.
       */
      if (isDikeDataColumnDef(column) &&
        (!currentSortable || currentSortable.sortedColumn.columnId !== column.columnId || currentSortable.type !== direction)) {
        this.dikeGrid.sorting.sortBy(column, direction);
      }
    }
  }

  onSetColumnSortable(): void {
    if (this.columnControl.valid) {
      // Get the selected column:
      const column = this.columnControl.value as DikeColumnDef;

      if (isDikeDataColumnDef(column)) {
        // Toggle the sortable flag for the selected column:
        this.dikeGrid.columnDef.setColumnSortable(column, !column.sortable);
      }
    }
  }

  onSortChange(sortedBy: DikeColumnSortEvent<Employee>): void {
    console.log('Sort change: ', sortedBy);
  }

  onColumnsChange(columns: DikeColumnDef[]): void {
    this.gridColumns = this.flatColumns(columns);
    // Clear selection:
    this.rowSortingForm.patchValue({ column: null, direction: '' });
  }

  private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
    return columns.reduce((accum, column) => {
      if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
        return [ ...accum, column, ...this.flatColumns(column.children) ];
      }

      return [ ...accum, column ];
    }, [ ]);
  }

  private createForm(): void {
    this.columnControl = new FormControl(null, Validators.required);

    this.rowSortingForm = new FormGroup({
      column: this.columnControl,
      direction: new FormControl({ value: '', disabled: false }, Validators.required),
    });
  }

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

```

{% endtab %}
{% endtabs %}


# Row Selection

The user can select one or all rows from the data source. This section describes how you can configure this feature.

## Live example

{% hint style="success" %}
[Row Selection](https://demos.dikesoft.com/dk-grid/row/selection) live example.
{% endhint %}

## Allowing the selection operation

To allow the user to **select/deselect** the DikeGrid rows, you must provide an input property named <mark style="color:orange;">`allowSelection`</mark>. This property is at the grid scope.

{% code title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    [allowSelection]="gridProperties.allowSelection">
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can change the value of the <mark style="color:orange;">`allowSelection`</mark> property at **runtime**.
{% endhint %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) for the live example and click on the **Allow Selection** checkbox.

![Floating Configuration Panel - Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FR0ZZq1Pf1grFV8IN86tZ%2Fselection-panel-conf.png?alt=media\&token=1756c6f8-11e7-499d-a420-228c9c2e57cb)

Once you have enabled selection, you will see a **checkbox** column displayed for every row. To **select/deselect** a row, click on the related checkbox.

![Enabling Row Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FvwEtfa5Q6uDtn8HQlO6S%2Frow-selection-enabled.png?alt=media\&token=b60b948f-ae0d-4e8a-8d75-7e8bd58e54bc)

## Selectable rows

By default, when you enable selection for the DikeGrid, all rows are susceptible to being selected.

If you want the user can select only **some rows**, you can provide a **function** specifying the conditions the rows must meet. You can set this function by providing a property named <mark style="color:orange;">`selectableRows`</mark>.

Let us define this function to allow the user to select rows whose age value is not 27.

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    [selectableRows]="selectableRowsDefinition">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
selectableRowsDefinition(entry: Employee): boolean {
    // Allow selection only for those rows whose age value is different of 27:
    return entry.age !== 27;
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
The <mark style="color:orange;">`selectableRows`</mark> property is of type [<mark style="color:green;">`SelectableFn`</mark>](/reference/dkgrid-api/dkgridselection#properties), and you can also provide this function using the **selection API**.
{% endhint %}

{% hint style="info" %}
You can change this function definition at **runtime**. Therefore, the DikeGrid instance will evaluate the currently selected rows, deselecting those rows that do not meet the new criteria.
{% endhint %}

As you can see, rows that do not meet the defined criteria display their checkbox **disabled**.

![Defining the selectableRows function](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FBVgDc5ps7XOzKv1Z2yUp%2Frow-selection-selectableFn.png?alt=media\&token=59368c0b-51ce-4570-af01-acc94dbafa72)

## Header Checkbox Selection

When you provide an **in-memory** data set, the DikeGrid instance always shows a checkbox in its header.

On the other hand, the DikeGrid instance will not show a checkbox in its header when you provide a **custom Datasource** because the DikeGrid does not know the total length of the provided data set.

{% hint style="success" %}
See the [Datasource](/fundamentals/datasource) section for further details about an **in-memory** data set and a **custom Datasource**.
{% endhint %}

By default, the checkbox in the header will select all rows even though the data set is filtered. However, if you want to select only the filtered rows, you can use the **selection API**.

## Group Selection

When you group rows by a column, every group will show its checkbox.

1. The selectable rows under the group will be selected when you click on the checkbox in the group. If the group has **non-selectable** rows, its state will be **indeterminate**.
2. When you click in any of the rows under the group, the checkbox in the group will update its status to indeterminate and become checked until you select all rows.

{% hint style="info" %}
The checkbox in the header will update its state as well.
{% endhint %}

We have enabled **row grouping** in the live demo to see this feature.

{% code title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    allowRowGrouping
    allowRowFiltering
    allowColumnDragging>
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        draggable
        groupable>
    </dike-grid-column>

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

{% endcode %}

As you can see, apart from allowing **row grouping**, we have enabled **column dragging**. Then, we set **Gender** and **Age** columns being **groupable**.&#x20;

You can move the **Age** column to the group panel with the previous configuration. Thus, you can select any group or a row under any group.

![Group Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FInW4AtuMRKPVL5V3qgiY%2Frow-selection-groups.png?alt=media\&token=bd1a87ed-249f-4547-a049-35d8415f7d09)

## DikeGrid Selection API

You can also use the **selection API** to select rows.

Before using the **selection API**, you must retrieve the related DikeGrid instance by querying the component's view.

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

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

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

{% endtab %}
{% endtabs %}

The **selection API** has the following methods:

| Method                 | Description                                                                                                                                                                                                            |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selectOne()`          | <ol><li>It marks the given row as selected. </li><li>The given row must pass the selectable function. </li><li>This method emits the <code>selectionChange</code> and <code>selectedRowChange</code> events.</li></ol> |
| `select()`             | <ol><li>It marks the given rows as selected. </li><li>It emits the <code>selectionChange</code> event.</li></ol>                                                                                                       |
| `deselectOne()`        | <ol><li>It turns off the selected flag for the given row.</li><li>This method emits the <code>selectionChange</code> and <code>deselectedRowChange</code> events.</li></ol>                                            |
| `deselect()`           | <ol><li>It turns off the selected flag for the given rows.</li><li>This method emits the <code>selectionChange</code> event.</li></ol>                                                                                 |
| `getSelectedRows()`    | This method returns an array of all selected rows.                                                                                                                                                                     |
| `getSelectedEntries()` | It returns all selected entries.                                                                                                                                                                                       |
| `selectAll()`          | This method will set all rows coming from the data source as selected.                                                                                                                                                 |
| `deselectAll()`        | It turns off the selection flag of all selected rows.                                                                                                                                                                  |
| `resetSelection()`     | This method deselects all selected rows and cleanses some internal variables. The DikeGrid instance will invoke this method every time you provide a new data set.                                                     |

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

{% hint style="info" %}
To use any previous methods, you must have allowed selection by providing the input property named <mark style="color:orange;">`allowSelection`</mark>.
{% endhint %}

Remember, when you provide a data source, the DikeGrid instance **wraps** every entry into an object of type <mark style="color:green;">`DikeGridRowEntry`</mark>. See the class hierarchy for the DikeGrid rows.

To see the **selection API** in action, we have created the following UI:

{% tabs %}
{% tab title="row-selection.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"
            [disabled]="!isFiltered"
            (click)="onSelectOne()">selectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onSelect()">select / filtered
        </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"
            [disabled]="!isFiltered"
            (click)="onDeselectOne()">deselectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onDeselect()">deselect / filtered
        </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)="onGetSelectedRows()">getSelectedRows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetSelectedEntries()">getSelectedEntries
        </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)="onSelectAll()">selectAll
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDeselectAll()">deselectAll
        </button>
    </div>
</div>

<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid">
</dike-grid>

```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
onSelectOne(): void {
  const filteredRows = this.dikeGrid.filter.getFilteredRows();

  if (filteredRows.length > 0) {
    this.dikeGrid.selection.selectOne(filteredRows[0]);
  }
}

onSelect(): void {
  const filteredRows = this.dikeGrid.filter.getFilteredRows();

  if (filteredRows.length > 0) {
    this.dikeGrid.selection.select(filteredRows);
  }
}

onDeselectOne(): void {
  const selectedFromFiltered = this.dikeGrid.filter.getFilteredRows().filter(row => row.selected);

  if (selectedFromFiltered.length > 0) {
    this.dikeGrid.selection.deselectOne(selectedFromFiltered[0]);
  }
}

onDeselect(): void {
  const filteredRows = this.dikeGrid.filter.getFilteredRows();

  if (filteredRows.length > 0) {
    this.dikeGrid.selection.deselect(filteredRows);
  }
}

onGetSelectedRows(): void {
  console.log('Selected rows: ', this.dikeGrid.selection.getSelectedRows());
}

onGetSelectedEntries(): void {
  console.log('Selected entries: ', this.dikeGrid.selection.getSelectedEntries());
}

onSelectAll(): void {
  this.dikeGrid.selection.selectAll();
}

onDeselectAll(): void {
  this.dikeGrid.selection.deselectAll();
}

```

{% endtab %}
{% endtabs %}

The previous HTML code generates the following output:

![Row Selection using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkWFkpQqW3ciWBUtTw4PC%2Frow-selection-ui-for-api.png?alt=media\&token=9e8f69e4-4317-4abb-a673-e9af34be626a)

For every button, we have attached the following actions:

1. Buttons **selectOne / filtered**, **select / filtered**, **deselectOne / filtered** and **deselect / filtered** work over the filtered set. These buttons are enabled if you have filtered the original set.
2. To allow filtering, we enabled the DikeGrid **Row Filter**.
3. The **selectAll** and **deselectAll** buttons are self-explanatory.
4. We printed out the **selected rows** and **selected entries** in the **dev console**.

Please, open the **live demo** and see the previous buttons in action.

To evaluate if the DikeGrid instance has a filter applied, we listen to the `filterChange` and `columnsChange` events.

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    (columnsChange)="onColumnsChange($event)"
    (filterChange)="onFilterChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
onFilterChange(filterable: DikeFilterable<Employee>): void {
  // We ignore the argument filterable because we are only interested in the action.
  this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
}

onColumnsChange(columns: DikeColumnDef[]): void {
  // We ignore the argument columns because we are only interested in the action.
  this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
}
```

{% endtab %}
{% endtabs %}

1. The `filterChange` event fires every time the user types something in the DikeGrid **Row Filter**.
2. We listen to the `columnsChange` event because every time the user groups the rows by a column, the user must move that column to the group panel, provoking the DikeGrid instance to raise the `columnsChange` event.

{% hint style="warning" %}
Remember that the DikeGrid ignores filters for the columns in the group panel.
{% endhint %}

## Selection events

Every time we **select** or **deselect** one or more rows, the DikeGrid instance emits the related event.

The following are the selection events:

| Event               | Description                                                |
| ------------------- | ---------------------------------------------------------- |
| `selectedRowChange` | It emits when the user selects one row.                    |
| `deselectRowChange` | It emits when the user deselects one row.                  |
| `selectionChange`   | It emits when the user selects/deselects one or more rows. |

We listen to these three events:

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    (selectedRowChange)="onSelectedRowChange($event)"
    (deselectedRowChange)="onDeselectedRowChange($event)"
    (selectionChange)="onSelectionChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
onSelectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
  console.log('Select one row change: ', row);
}

onDeselectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
  console.log('Deselect one row change: ', row);
}

onSelectionChange(rows: DikeGridDataRowEntry<Employee>[]): void {
  console.log('Selection change: ', rows);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
Please, open the **dev console** to see the output of these events.
{% endhint %}

{% hint style="info" %}
You can also listen to these events from the [<mark style="color:green;">`DikeGridSelection`</mark>](/reference/dkgrid-api/dkgridselection#events) instance.
{% endhint %}

## Summary

To perform the **selection** operation, you must allow it by providing an input property named <mark style="color:orange;">`allowSelection`</mark>. By default, the user can select all rows, but you can enable only some rows for selection by providing a **custom function** where every row must meet the established criteria. You can **select** or **deselect** rows through the **UI** or the **selection API**.

### Complete code for this section

{% tabs %}
{% tab title="row-selection.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"
            [disabled]="!isFiltered"
            (click)="onSelectOne()">selectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onSelect()">select / filtered
        </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"
            [disabled]="!isFiltered"
            (click)="onDeselectOne()">deselectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onDeselect()">deselect / filtered
        </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)="onGetSelectedRows()">getSelectedRows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetSelectedEntries()">getSelectedEntries
        </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)="onSelectAll()">selectAll
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDeselectAll()">deselectAll
        </button>
    </div>
</div>

<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (columnsChange)="onColumnsChange($event)"
    (filterChange)="onFilterChange($event)"
    (selectedRowChange)="onSelectedRowChange($event)"
    (deselectedRowChange)="onDeselectedRowChange($event)"
    (selectionChange)="onSelectionChange($event)"
    [selectableRows]="selectableRowsDefinition"

    allowRowGrouping
    allowRowFiltering
    allowColumnDragging
    [allowSelection]="gridProperties.allowSelection"
    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250">
    </dike-grid-column>

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150">
        </dike-grid-column>

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

    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        draggable
        groupable>
    </dike-grid-column>

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

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

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

</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

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

import { Subscription } from 'rxjs';

import { DikeGridComponent, DikeGridDataRowEntry, 
  DikeGridDataSourceInput, DikeFilterable, DikeColumnDef 
} 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: 'row-selection',
  templateUrl: './row-selection.component.html',
  styleUrls: ['./row-selection.component.scss'],

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  isFiltered: boolean;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

    this.isFiltered = false;
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

  selectableRowsDefinition(entry: Employee): boolean {
    // Allow selection only for those rows whose age value is different of 27:
    return entry.age !== 27;
  }

  onSelectOne(): void {
    const filteredRows = this.dikeGrid.filter.getFilteredRows();

    if (filteredRows.length > 0) {
      this.dikeGrid.selection.selectOne(filteredRows[0]);
    }
  }

  onSelect(): void {
    const filteredRows = this.dikeGrid.filter.getFilteredRows();

    if (filteredRows.length > 0) {
      this.dikeGrid.selection.select(filteredRows);
    }
  }

  onDeselectOne(): void {
    const selectedFromFiltered = this.dikeGrid.filter.getFilteredRows().filter(row => row.selected);

    if (selectedFromFiltered.length > 0) {
      this.dikeGrid.selection.deselectOne(selectedFromFiltered[0]);
    }
  }

  onDeselect(): void {
    const filteredRows = this.dikeGrid.filter.getFilteredRows();

    if (filteredRows.length > 0) {
      this.dikeGrid.selection.deselect(filteredRows);
    }
  }

  onGetSelectedRows(): void {
    console.log('Selected rows: ', this.dikeGrid.selection.getSelectedRows());
  }

  onGetSelectedEntries(): void {
    console.log('Selected entries: ', this.dikeGrid.selection.getSelectedEntries());
  }

  onSelectAll(): void {
    this.dikeGrid.selection.selectAll();
  }

  onDeselectAll(): void {
    this.dikeGrid.selection.deselectAll();
  }

  onFilterChange(filterable: DikeFilterable<Employee>): void {
    // We ignore the argument filterable because we are only interested in the action.
    this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
  }

  onColumnsChange(columns: DikeColumnDef[]): void {
    // We ignore the argument columns because we are only interested in the action.
    this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
  }

  onSelectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
    console.log('Select one row change: ', row);
  }

  onDeselectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
    console.log('Deselect one row change: ', row);
  }

  onSelectionChange(rows: DikeGridDataRowEntry<Employee>[]): void {
    console.log('Selection change: ', rows);
  }

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

{% endtab %}
{% endtabs %}


# Row Grouping

This section describes how the user can display rows grouped by columns.

## Live example

{% hint style="success" %}
[Row Grouping](https://demos.dikesoft.com/dk-grid/row/grouping) live example.
{% endhint %}

## Allowing the Row-Grouping operation

To allow the user to **group** the DkGrid rows, you must provide an input property named <mark style="color:orange;">`allowRowGrouping`</mark>.

{% code title="row-grouping.component.html" %}

```markup
<dike-grid id="grid-row-grouping" height="700px" #grid="dkgGrid"
    allowRowGrouping>
</dike-grid>
```

{% endcode %}

{% hint style="warning" %}
You can set the value of the <mark style="color:orange;">`allowRowGrouping`</mark> property only at creation time.
{% endhint %}

After enabling the row-grouping operation, you will see a panel above the column headers. This panel is the **group panel**.

![Group panel](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FYCQdbtrLfFedbQ8zouYa%2Frow-grouping-group-panel.png?alt=media\&token=3eb2df40-26bd-4c54-b579-3bfb706ffe5a)

## Row Grouping through the UI

To allow the user to move columns to the **group panel**, you must provide an input property named <mark style="color:orange;">`allowColumnDragging`</mark>. Then, mark the columns you want the DkGrid rows grouped by as **draggable** and **groupable**.

{% code title="row-grouping.component.html" %}

```markup
<dk-grid id="grid-row-grouping" height="700px" #grid="dkGrid"
    allowRowGrouping
    [allowColumnDragging]="gridProperties.allowColumnDragging">

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

    <dk-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250"
        groupable
        draggable>
    </dk-grid-column>

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

        <dk-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            groupable
            draggable>
        </dk-grid-column>

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

    </dk-grid-column>

    <dk-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        draggable
        groupable>
    </dk-grid-column>

    <dk-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="100"
        draggable
        groupable>
    </dk-grid-column>

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

    <dk-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        width="150">
    </dk-grid-column>

</dk-grid>
```

{% endcode %}

We have defined **Country**, **Age**, **Gender,** and **Name** columns as **draggable** and **groupable**.

{% hint style="info" %}
Remember, you should not **lock** a column if you want to **drag** that column.
{% endhint %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) and mark the **Allow Column Dragging** checkbox.

![Floating Configuration Panel - Allow Column Dragging](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fv8NyyoupJ0lcSqptEayo%2Fdragging-panel-conf.png?alt=media\&token=982a2a66-6959-4cc4-8515-2cbcd9d9beca)

Let us group the DkGrid rows by the **Country** and **Gender** columns.

![Grouped by Country and Gender columns](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FTqzaUavUyqJlN4ZSfJ1r%2Frow-grouping-country-gender.gif?alt=media\&token=7464bcf4-bbc8-4d87-be70-655651ca3f3c)

### Custom templates

As discussed in the [Column Definitions](/columns/column-definitions#templates-for-displaying-field-values) section, every column has a **default template** for displaying the corresponding field value for every row.

Every group row will take the default template for displaying its value after grouping the rows. However, you can change the default template, as you can see in the following code snippet:

{% code title="row-grouping.component.html" %}

```markup
<dk-grid id="grid-row-grouping" height="700px" #grid="dkGrid">
    <dk-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250"
        [displayTemplate]="country"
        groupable
        draggable>
    </dk-grid-column>
    
    <dk-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        [displayTemplate]="gender"
        draggable
        groupable>
    </dk-grid-column>
</dk-grid>

<ng-template #country let-value="fieldValue">
    <div class="grid grid-cols-country-flag gap-2">
        <img class="my-0" [src]="value | countryFlag" onerror="this.src='assets/images/flags/none.png'">
        <p [matTooltip]="value">{{ value }}</p>
    </div>
</ng-template>

<ng-template #gender let-value="fieldValue">
    <mat-icon [matTooltip]="value">{{ value }}</mat-icon>
</ng-template>
```

{% endcode %}

{% hint style="warning" %}
When defining a template to use in the group rendering, the related inner component will not pass the **row entry** object when calling the specified template.
{% endhint %}

After grouping the DkGrid rows by the **Country** and **Gender** columns, you will see the following output:

![Country and Gender columns with custom templates](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fwqcd6AdztFYqGZH1XJMx%2Frow-grouping-country-gender-custom-templates.png?alt=media\&token=bd5b1f0a-2192-440a-becf-56f20f871bef)

{% hint style="success" %}
We have defined a custom pipe named **countryFlag** to map the country name with its corresponding flag file.
{% endhint %}

### Clearing the group panel

You can remove columns from the group panel one by one or all of them in one action.

You can click on the **close icon** next to the column name to remove that column from the group panel. The action will **move** the column from the group panel to the **center panel**.

![Removing one column from the group panel](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkV0wQjI2yFx7rFHAhLky%2Frow-grouping-remove-one-column.gif?alt=media\&token=b8721486-5824-4a8a-8d59-bdb950a2efb9)

Or, you can remove all columns by clicking on the **group panel icon** at the left of the panel itself. The action will **move** all the columns from the group panel to the **center panel**.

![Removing all columns from the group panel to the center panel](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F51lOFFoaa8xaNvYZcZqR%2Frow-grouping-clear-panel.gif?alt=media\&token=1c83b114-9d69-414a-ab5d-0ab754f274ac)

## Group Selection

You can open the [Floating Configuration Panel](/#floating-configuration-panel) and click on the **Allow Selection** checkbox to enable selection.

![Floating Configuration Panel - Allow Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FR0ZZq1Pf1grFV8IN86tZ%2Fselection-panel-conf.png?alt=media\&token=1756c6f8-11e7-499d-a420-228c9c2e57cb)

{% hint style="success" %}
See the [Row Selection - Group Selection](/rows/row-selection#group-selection) section for further details about group selection.
{% endhint %}

## Row Grouping using the API

You can use the **column API** to move columns to, from, or inside the group panel, and you can use the **rowGrouping API** to clear columns from the group panel.

{% hint style="info" %}
When you group the DkGrid rows by some columns, it is enough to define them as **draggable**, **groupable**, and not **locked** when using the **API**. Therefore, it does not matter if you do not provide the input property <mark style="color:orange;">`allowColumnDragging`</mark>.
{% endhint %}

Before using the **API**, you must retrieve the corresponding DkGrid instance by querying the component's view.

{% tabs %}
{% tab title="row-grouping.component.html" %}

```markup
<dk-grid id="grid-row-grouping" height="700px" #grid="dkGrid">
</dk-grid>
```

{% endtab %}

{% tab title="row-grouping.component.ts" %}

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

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

{% endtab %}
{% endtabs %}

{% hint style="success" %}
The **Row-Grouping API** is an instance of type <mark style="color:green;">`DkGridRowGrouping`</mark> under the [<mark style="color:green;">`DikeGridComponent`</mark>](/reference/components/dkgridcomponent) through the **read-only** property named `rowGrouping`.
{% endhint %}

The **rowGrouping API** has the following method:

| Method               | Description                                                                 |
| -------------------- | --------------------------------------------------------------------------- |
| `clearRowGrouping()` | This method will move all columns from the group panel to the center panel. |

{% hint style="success" %}
For further details, see the [<mark style="color:green;">`DikeGridRowGrouping`</mark>](/reference/dkgrid-api/dkgridrowgrouping) definition.
{% endhint %}

{% hint style="success" %}
For further details about the **column API**, see the [<mark style="color:green;">`DikeGridColumnDef`</mark>](/reference/dkgrid-api/dkgridcolumndef) definition.
{% endhint %}

To see the **API** in action, consider the following UI additions:

{% tabs %}
{% tab title="row-grouping.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill" class="flex-none w-56 m-2">
        <mat-label>Content panels</mat-label>
        <mat-select [formControl]="columnControl" required>
            <mat-select-trigger>
                <div class="w-40 flex flex-row items-center justify-between">
                    <div>{{ columnControl.value?.headerText }}</div>
                    <mat-icon *ngIf="columnControl.value && columnControl.value.draggable && columnControl.value.groupable && !columnControl.value.locked">list</mat-icon>
                </div>
            </mat-select-trigger>
            <mat-option *ngFor="let column of contentColumns" [value]="column">
                <div class="flex flex-row items-center justify-between">
                    <div>{{ column.headerText }}</div>
                    <mat-icon *ngIf="column.draggable && column.groupable && !column.locked">list</mat-icon>
                </div>
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-form-field appearance="fill" class="flex-none w-56 m-2">
        <mat-label>Group panel</mat-label>
        <mat-select [formControl]="groupByColumnControl" required>
            <mat-option *ngFor="let column of groupColumns" [value]="column">
                {{ column.headerText }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <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"
            [disabled]="!columnControl.valid"
            (click)="onGroupBy()">Group by
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!groupByColumnControl.valid"
            (click)="onMoveColumn()">Move to center panel
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isDikeGridGroupedBy"
            (click)="onClearGroupPanel()">Clear group panel
        </button>
    </div>
</div>

<dike-grid id="grid-row-grouping" height="700px" #grid="dkgGrid">
</dike-grid>

```

{% endtab %}

{% tab title="row-grouping.component.ts" %}

```typescript
onGroupBy(): void {
  if (this.columnControl.valid) {
    const contentPanelColumn = this.columnControl.value as DikeColumnDef;
    this.dikeGrid.columnDef.moveColumnIntoPanel(contentPanelColumn, 'groupPanel');
  }
}

onMoveColumn(): void {
  if (this.groupByColumnControl.valid) {
    const groupPanelColumn = this.groupByColumnControl.value as DikeColumnDef;
    this.dikeGrid.columnDef.moveColumnIntoPanel(groupPanelColumn, 'centerPanel');
  }
}

onClearGroupPanel(): void {
  this.dikeGrid.rowGrouping.clearRowGrouping();
}
```

{% endtab %}
{% endtabs %}

The previous HTML code generates the following output:

![Row Grouping using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fl0XxKjAKyE9HJ79ROVMe%2Frow-grouping-ui-for-api.png?alt=media\&token=366f0037-aaba-4d9d-ae79-e74e8af59788)

With the previous UI, you can do the following actions:

1. First, you can choose a column from the **Content panels** dropdown control. **Draggable**, **groupable**, and **not locked** columns have an icon indicating they can group the DikeGrid rows. After selecting a column, you can click on the **Group by** button, moving the column to the group panel.
2. The **Group panel** dropdown control contains the columns in the group panel. You can choose a column and move it to the center panel by clicking on the related button.
3. You can click on the **Clear group panel** button to move all the columns from the group panel to the center panel.

Be aware that we left all the columns from the content panels enabled, even the column group named **Complete Name**.

{% hint style="warning" %}
You cannot move column groups to the group panel from the UI. Nevertheless, you can use the **column API** to move column groups to the group panel. The column API takes all the data columns from the given column group and inserts those defined as **groupable**, **draggable**, and **not locked**.
{% endhint %}

It is essential to notice that we are **listening** to any change in the **DikeGrid's columns**, attaching a callback to the `contentPanelsColumnsChange` and `groupPanelColumnsChange` events.

{% tabs %}
{% tab title="row-grouping.component.html" %}

```markup
<dike-grid id="grid-row-grouping" height="700px" #grid="dkgGrid"
    (contentPanelsColumnsChange)="onContentColumnsChange($event)"
    (groupPanelColumnsChange)="onGroupColumnsChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-grouping.component.ts" %}

```typescript
onContentColumnsChange(columns: DikeColumnDef[]): void {
  this.contentColumns = this.flatColumns(columns);
  // Clear the selected column:
  this.columnControl.patchValue(null);
}

onGroupColumnsChange(columns: DikeColumnDef[]): void {
  this.groupColumns = this.flatColumns(columns);
  // Clear the selected column:
  this.groupByColumnControl.patchValue(null);
  // Update the flag to let us know if the DikeGrid rows are grouped:
  this.isDikeGridGroupedBy = columns.length > 0;
}

```

{% endtab %}
{% endtabs %}

Every time we receive a new set of columns, we flat them into a single array, invoking the method `flatColumns()`. So indeed, we are **flattening** the **column groups**, traversing them **recursively**.

{% code title="row-grouping.component.ts" %}

```typescript
private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
  return columns.reduce((accum, column) => {
    if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
      return [ ...accum, column, ...this.flatColumns(column.children) ];
    }

    return [ ...accum, column ];
  }, [ ]);
}

```

{% endcode %}

If you see the items of the **Content panels** **dropdown** control, you see the columns **Employee Id**, **Country**, **Complete Name**, **Name**, **Surname**, etc.

![Flattening column groups](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fz5aiHAOCpZfmXvYvEkxi%2Frow-grouping-flattening-groups.png?alt=media\&token=80e8634e-f54c-4a45-b67e-251ef45c4231)

## Customizing the group panel

You can change the **group panel** **height**, the **height of the columns** displayed in the group panel, and the **Row-Grouping indent width**. Indeed, you can hide the Row-Grouping indent.

{% hint style="success" %}
For further details, see the [Grid Structure - Group panel section](/fundamentals/grid-structure/panels#group-panel).
{% endhint %}

You can open the [Floating Configuration Panel](/#floating-configuration-panel) for the **live demo** and change any mentioned properties.

## Empty values

When the DikeGrid instance groups the rows, the following values are considered empties:

| DikeColumnDataType  | Values                                                                      |
| ------------------- | --------------------------------------------------------------------------- |
| `Text` and `Binary` | `null`, `undefined`, and empty <mark style="color:green;">`strings`</mark>. |
| `Numeric`           | `null`, `undefined`, and <mark style="color:green;">`NaN`</mark> values.    |
| `Date`              | `null`, `undefined`, and <mark style="color:red;">`Invalid Dates`</mark>.   |

{% hint style="info" %}
We recommend you define the **getter function** for the columns you define. For further details, see the [Column Definitions](/columns/column-definitions#retrieving-and-updating-a-field-value) section.
{% endhint %}

## Row-Grouping events

Regarding row grouping operation, the DikeGrid instance emits the following events:

| Event                     | Description                                                                                |
| ------------------------- | ------------------------------------------------------------------------------------------ |
| `groupPanelColumnsChange` | Every time the user moves a column to, from, and inside the group panel, this event emits. |
| `collapseGroupRowChange`  | It emits when the user opens or closes a group row.                                        |

We listen to these two events at the grid scope:

{% tabs %}
{% tab title="row-grouping.component.html" %}

```markup
<dike-grid id="grid-row-grouping" height="700px" #grid="dkgGrid"
    (groupPanelColumnsChange)="onGroupColumnsChange($event)"
    (collapseGroupRowChange)="onCollapseGroupRowChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-grouping.component.ts" %}

```typescript
onGroupColumnsChange(columns: DikeColumnDef[]): void {
  this.groupColumns = this.flatColumns(columns);
  // Clear the selected column:
  this.groupByColumnControl.patchValue(null);
  // Update the flag to let us know if the DikeGrid rows are grouped:
  this.isDikeGridGroupedBy = columns.length > 0;
}
  
onCollapseGroupRowChange(event: DikeGroupRowCollapseEvent<Employee>): void {
  console.log('DikeGroupRowCollapseEvent: ', event);
}

```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
You can listen to these events from the [<mark style="color:green;">`DikeGridRowGrouping`</mark>](/reference/dkgrid-api/dkgridrowgrouping#events) instance.
{% endhint %}

{% hint style="success" %}
Please, open the **dev console** to see the output of the `collapseGroupRowChange` event.
{% endhint %}

## Summary

Firstly, you must allow the **Row-Grouping** operation at the grid scope by providing an input property named <mark style="color:orange;">`allowRowGrouping`</mark>. Then, to group the DikeGrid rows by a column, you must define that column as **draggable**, **groupable**, and **not locked**.

For row grouping, through the **UI**, you must provide the input property named <mark style="color:orange;">`allowColumnDragging`</mark>.

### Complete code for this section

{% tabs %}
{% tab title="row-grouping.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill" class="flex-none w-56 m-2">
        <mat-label>Content panels</mat-label>
        <mat-select [formControl]="columnControl" required>
            <mat-select-trigger>
                <div class="w-40 flex flex-row items-center justify-between">
                    <div>{{ columnControl.value?.headerText }}</div>
                    <mat-icon *ngIf="columnControl.value && columnControl.value.draggable && columnControl.value.groupable && !columnControl.value.locked">list</mat-icon>
                </div>
            </mat-select-trigger>
            <mat-option *ngFor="let column of contentColumns" [value]="column">
                <div class="flex flex-row items-center justify-between">
                    <div>{{ column.headerText }}</div>
                    <mat-icon *ngIf="column.draggable && column.groupable && !column.locked">list</mat-icon>
                </div>
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-form-field appearance="fill" class="flex-none w-56 m-2">
        <mat-label>Group panel</mat-label>
        <mat-select [formControl]="groupByColumnControl" required>
            <mat-option *ngFor="let column of groupColumns" [value]="column">
                {{ column.headerText }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <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"
            [disabled]="!columnControl.valid"
            (click)="onGroupBy()">Group by
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!groupByColumnControl.valid"
            (click)="onMoveColumn()">Move to center panel
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isDikeGridGroupedBy"
            (click)="onClearGroupPanel()">Clear group panel
        </button>
    </div>
</div>

<dike-grid id="grid-row-grouping" height="700px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    allowRowGrouping
    [allowColumnDragging]="gridProperties.allowColumnDragging"
    [allowSelection]="gridProperties.allowSelection"

    (contentPanelsColumnsChange)="onContentColumnsChange($event)"
    (groupPanelColumnsChange)="onGroupColumnsChange($event)"
    (collapseGroupRowChange)="onCollapseGroupRowChange($event)"

    [datasource]="dkgDataSource">

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

    <dike-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250"
        [displayTemplate]="country"
        groupable
        draggable>
    </dike-grid-column>

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            groupable
            draggable>
        </dike-grid-column>

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

    </dike-grid-column>

    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        [displayTemplate]="gender"
        draggable
        groupable>
    </dike-grid-column>

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

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

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

</dike-grid>

<ng-template #country let-value="fieldValue">
    <div class="grid grid-cols-country-flag gap-2">
        <img class="my-0" [src]="value | countryFlag" onerror="this.src='assets/images/flags/none.png'">
        <p [matTooltip]="value">{{ value }}</p>
    </div>
</ng-template>

<ng-template #gender let-value="fieldValue">
    <mat-icon [matTooltip]="value">{{ value }}</mat-icon>
</ng-template>

```

{% endtab %}

{% tab title="row-grouping.component.ts" %}

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

import { Subscription } from 'rxjs';

import { DikeColumnDef, DikeGridComponent, DikeGridDataSourceInput, DikeGroupRowCollapseEvent, isDikeGroupColumnDef } 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 { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  columnControl: FormControl;
  groupByColumnControl: FormControl;

  contentColumns: DikeColumnDef[];
  groupColumns: DikeColumnDef[];
  isDikeGridGroupedBy: boolean;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

    this.isDikeGridGroupedBy = false;
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();

    this.columnControl = new FormControl(null, Validators.required);
    this.groupByColumnControl = new FormControl(null, Validators.required);
  }

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

  onContentColumnsChange(columns: DikeColumnDef[]): void {
    this.contentColumns = this.flatColumns(columns);
    // Clear the selected column:
    this.columnControl.patchValue(null);
  }

  onGroupColumnsChange(columns: DikeColumnDef[]): void {
    this.groupColumns = this.flatColumns(columns);
    // Clear the selected column:
    this.groupByColumnControl.patchValue(null);
    // Update the flag to let us know if the DikeGrid rows are grouped:
    this.isDikeGridGroupedBy = columns.length > 0;
  }

  onGroupBy(): void {
    if (this.columnControl.valid) {
      const contentPanelColumn = this.columnControl.value as DikeColumnDef;
      this.dikeGrid.columnDef.moveColumnIntoPanel(contentPanelColumn, 'groupPanel');
    }
  }

  onMoveColumn(): void {
    if (this.groupByColumnControl.valid) {
      const groupPanelColumn = this.groupByColumnControl.value as DikeColumnDef;
      this.dikeGrid.columnDef.moveColumnIntoPanel(groupPanelColumn, 'centerPanel');
    }
  }

  onClearGroupPanel(): void {
    this.dikeGrid.rowGrouping.clearRowGrouping();
  }

  onCollapseGroupRowChange(event: DikeGroupRowCollapseEvent<Employee>): void {
    console.log('DikeGroupRowCollapseEvent: ', event);
  }

  private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
    return columns.reduce((accum, column) => {
      if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
        return [ ...accum, column, ...this.flatColumns(column.children) ];
      }

      return [ ...accum, column ];
    }, [ ]);
  }

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

```

{% endtab %}
{% endtabs %}


# Row Pagination

This section describes how to display a fixed amount of rows, in other words, page by page.

## Live example

{% hint style="success" %}
[Row Pagination](https://demos.dikesoft.com/dk-grid/row/pagination) live example.
{% endhint %}

## Allowing pagination

To allow the user to paginate the DikeGrid rows, you must provide an input property named <mark style="color:orange;">`allowPagination`</mark> at the grid scope.

{% code title="row-pagination.component.html" %}

```markup
<dike-grid id="grid-row-pagination" height="700px" #grid="dkgGrid"
    [allowPagination]="gridProperties.allowPagination"
    [pageSize]="gridProperties.pageSize">
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can change the value of the <mark style="color:orange;">`allowPagination`</mark> property at **runtime**.
{% endhint %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) and click on the **Allow Pagination** checkbox.

![Floating Configuration Panel - Pagination](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4rVJTuGq2Tse1P1FPZWE%2Fpagination-panel-conf.png?alt=media\&token=47cd5555-0451-4a04-95c1-b5b28a3ec573)

After enabling the pagination, you will see the DikeGrid **paginator** at the bottom of the content rows.

![Enabling Row Pagination](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FjPINsr873JtGwY8UMX0r%2Frow-pagination-enabled.png?alt=media\&token=417ccd51-332b-463a-b9c5-e245c6a73119)

## Page size

By default, the page size is **50**. You can change this value by providing an input property named <mark style="color:orange;">`pageSize`</mark>.

As you can see in the previous code snippet, we have bound the <mark style="color:orange;">`pageSize`</mark> property to the `gridProperties` object. Therefore, you can open the [Floating Configuration Panel](/#floating-configuration-panel) and change the value for the **page size**.

## Pagination Row

Since the DikeGrid structure uses the **CSS Grid Layout** specification, the paginator is called **Pagination Row**. Indeed, the Pagination Row is the **template-row** definition where the paginator lives.

{% hint style="info" %}
You can change the **height** of the **Pagination Row**. For further details, see the [Grid Structure - Paginator](/fundamentals/grid-structure/paginator#changing-the-pagination-row-height) section.
{% endhint %}

For this section, we have bound the input property <mark style="color:orange;">`paginationRowHeight`</mark> to the related property from the `gridProperties` object.

{% code title="row-pagination.component.html" %}

```markup
<dike-grid id="grid-row-pagination" height="600px" #grid="dkgGrid"
    [paginationRowHeight]="gridProperties.paginationHeight">
</dike-grid>
```

{% endcode %}

## DikeGrid Pagination API

The DikeGrid internally uses the **MatPaginator** component. Therefore, the DikeGrid definition **wraps** the MatPaginator instance.

Before accessing and using the **pagination API**, you must retrieve the related DikeGrid instance by querying the component's view.

{% tabs %}
{% tab title="row-pagination.component.html" %}

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

{% endtab %}

{% tab title="row-pagination.component.ts" %}

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

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

{% endtab %}
{% endtabs %}

{% hint style="success" %}
The **pagination API** is an instance of type [<mark style="color:green;">`DikeGridPagination`</mark>](/reference/dkgrid-api/dkgridpagination) under the <mark style="color:green;">`DikeGridComponent`</mark> through the **read-only** property named <mark style="color:orange;">`pagination`</mark>.
{% endhint %}

The DikeGrid **pagination API** exposes the **MatPaginator** instance through a **read-only** property named **matPaginator**.

{% hint style="info" %}
Retrieving and using the DikeGrid **pagination API** helps in defining a **custom Datasource**.
{% endhint %}

## Pagination events

Every time the user navigates through the available pages, the DikeGrid instance emits a page event.

Since you can allow the pagination at runtime, the pagination API exposes an event that fires the MatPaginator instance.

The **pagination API** exposes the following events:

| Event             | Description                                                                   |
| ----------------- | ----------------------------------------------------------------------------- |
| `pageChange`      | This event fires every time the user navigates through the available pages.   |
| `paginatorChange` | This event fires when the user allows or disallows the DikeGrid's pagination. |

We are listening to both events:

{% tabs %}
{% tab title="row-pagination.component.html" %}

```markup
<dike-grid id="grid-row-pagination" height="600px" #grid="dkgGrid"
    (pageChange)="onPageChange($event)"
    (paginatorChange)="onPaginatorChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-pagination.component.ts" %}

```typescript
onPageChange(page: PageEvent): void {
  console.log('Page change: ', page);
}

onPaginatorChange(paginator: MatPaginator): void {
  console.log('Paginator change: ', paginator);
}
  
```

{% endtab %}
{% endtabs %}

For the `pageChange` event, the emitted object is the same as the **MatPaginator** sends. For further details, see the [Angular Material official](https://material.angular.io/components/paginator/api) docs.

{% hint style="success" %}
You can also listen to the pagination events from the [<mark style="color:green;">`DikeGridPagination`</mark>](/reference/dkgrid-api/dkgridpagination#events) instance.
{% endhint %}

{% hint style="success" %}
Open the **dev console** to see the output of the pagination events.
{% endhint %}

## Summary

To perform the pagination operation, you must allow it by providing an input property named <mark style="color:orange;">`allowPagination`</mark>. The DikeGrid definition internally uses the **MatPaginator** component. Therefore, every DikeGrid instance exposes its corresponding MatPaginator instance through the **pagination API**.

### Complete code for this section

{% tabs %}
{% tab title="row-pagination.component.html" %}

```markup
<dike-grid id="grid-row-pagination" height="600px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"
    
    (pageChange)="onPageChange($event)"
    (paginatorChange)="onPaginatorChange($event)"
    
    [allowPagination]="gridProperties.allowPagination"
    [pageSize]="gridProperties.pageSize"
    [paginationRowHeight]="gridProperties.paginationHeight"
    
    [datasource]="dkgDataSource">
    
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350">
    </dike-grid-column>

    <dike-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250">
    </dike-grid-column>

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150">
        </dike-grid-column>

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

    </dike-grid-column>

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

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

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

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

</dike-grid>
```

{% endtab %}

{% tab title="row-pagination.component.ts" %}

```typescript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';

import { Subscription } from 'rxjs';

import { DikeGridComponent, DikeGridDataSourceInput } 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 { DikeGridConfig } from 'app/services/dike-grid.config.service';
import { SampleData } from 'app/services/sample-data.service';

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

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(10000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

  onPageChange(page: PageEvent): void {
    console.log('Page change: ', page);
  }
  
  onPaginatorChange(paginator: MatPaginator): void {
    console.log('Paginator change: ', paginator);
  }

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

```

{% endtab %}
{% endtabs %}


# Column Filters

This section describes how to enable filtering for a column and what events are involved.

## Live example

{% hint style="success" %}
[Column Filters](https://demos.dikesoft.com/dk-grid/filtering/column-filters) live example.
{% endhint %}

## Defining a column as filterable

If you want to filter the DikeGrid rows by a column, you must provide an input property named <mark style="color:orange;">`filterable`</mark> for that column definition.

Consider the following column definitions:

{% tabs %}
{% tab title="column-filters.component.html" %}

```markup
<dike-grid id="grid-col-filters" height="600px" #grid="dkgGrid"
    (gridColumnDefInstance)="onColumnDefInstance($event)">
    
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350"
        order="1"
        sortable>
    </dike-grid-column>

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            filterable>
        </dike-grid-column>

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

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

```

{% endtab %}

{% tab title="column-filters.component.ts" %}

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnFiltersComponent implements OnInit, OnDestroy {

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Age column:
    const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
    ageColumn.order = 4;
    ageColumn.width = 100;
    ageColumn.sortable = true;
    ageColumn.filterable = true;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.order = 5;
    hireDateColumn.width = 120;
    hireDateColumn.sortable = true;
    hireDateColumn.filterable = true;

    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.order = 6;
    emailColumn.width = 250;
    emailColumn.sortable = true;

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns([ ageColumn, hireDateColumn, emailColumn ]);
  }
}
```

{% endtab %}
{% endtabs %}

As you can see, we defined the columns **Name**, **Age**, and **Hire Date** as **filterable**.

If you open the [Column Context Menu](/fundamentals/grid-structure/column-context-menu) for a **filterable** column, you will see the filter option available.

![Column Context Menu - Fitler option](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FZa7lVPNvjDbt2bgenZQk%2Fcolumn-filters-column-context-menu.png?alt=media\&token=973d64ac-3ebd-43e1-b412-e93c20252ba7)

{% hint style="warning" %}
You can not define a **column group** as filterable.
{% endhint %}

## Initial filter

You can set an initial filter when defining a column.

See the following code:

{% tabs %}
{% tab title="column-filters.component.html" %}

```markup
<dike-grid id="grid-col-filters" height="600px" #grid="dkgGrid">
    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name"
        order="2">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            filterable
            [filter]="[{ condition: 'startsWith', logicalOperator: 'and', value: 'w' }]">
        </dike-grid-column>

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

```

{% endtab %}

{% tab title="column-filters.component.ts" %}

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnFiltersComponent implements OnInit, OnDestroy {

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Age column:
    const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
    ageColumn.order = 4;
    ageColumn.width = 100;
    ageColumn.sortable = true;
    ageColumn.filterable = true;
    ageColumn.groupable = true;
    ageColumn.filter = [{
        condition: ConditionType.GREATER_THAN,
        logicalOperator: 'and',
        value1: 50
    }];

    //...

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns([ ageColumn, hireDateColumn, emailColumn ]);
  }
}

```

{% endtab %}
{% endtabs %}

We have set an initial filter for the **Name** column in the **HTML**. For the **Age** column, we defined the initial filter in **code**.

Open the [Column Context Menu](/fundamentals/grid-structure/column-context-menu) for the **Name** or the **Age** column. You will see the filter we have set at creation time.

![Initial filter for the Name column](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FgqtIiraSWa4qOaakbuAF%2Fcolumn-filters-initial-filter.png?alt=media\&token=ad963696-cbdf-4bd4-80cd-3ee92ee2608b)

The type of the object for setting an initial filter depends on the type of the column. For example, the **Age** column is a <mark style="color:red;">`Numeric`</mark> column. Thus, its filter property is of type <mark style="color:green;">`DikeNumericFilter[]`</mark>.

When defining an initial filter in the **HTML** template, the filter property is of type <mark style="color:green;">`DikeFilter[]`</mark>.

{% hint style="success" %}
See the [<mark style="color:green;">`DikeFilter`</mark>](/reference/interfaces/filtering#dikefilter) hierarchy definition.
{% endhint %}

## Modifying filters

You can **add** or **remove** filter conditions for any column defined as **filterable** through the [Column Context Menu](/fundamentals/grid-structure/column-context-menu).

Let us add a condition for the **Hire Date** column:

![Adding conditions](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F11yNt8bfQpH91pQZXiuA%2Fcolumn-filters-addding-conditions.gif?alt=media\&token=8b0a192f-d60a-43ca-b08e-f0342152d19c)

{% hint style="info" %}
You can add **any** number of conditions joined by logical operators (**OR**/**AND**).
{% endhint %}

## Filtering execution

### Debounce time

<mark style="color:red;">`Text`</mark> and <mark style="color:red;">`Numeric`</mark> columns have a **debounce time** when the user is typing a value to filter the DikeGrid rows. By default, the debounce time is **400** milliseconds.

![Filtering - Debounce time](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4gleYMBen6z30nWhGovA%2Fcolumn-filters-debounce-time.gif?alt=media\&token=e5c8d61a-571d-4d64-b4c5-5fc65e8e1ef5)

As you can see, once the time has elapsed, the column sends the filter for its execution.

{% hint style="success" %}
You can change the time you wait for filter execution by providing the Injection Tokens <mark style="color:blue;">`TEXT_FILTER_DEBOUNCE_TIME`</mark> and <mark style="color:blue;">`NUMERIC_FILTER_DEBOUNCE_TIME`</mark>.
{% endhint %}

Let us change the debounce time for `Numeric` types.

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

```typescript
@NgModule({
  providers: [
    { provide: NUMERIC_FILTER_DEBOUNCE_TIME, useValue: 300 }
  ]
})
export class FilteringModule { }

```

{% endcode %}

### Filtering on-demand

If you want to control when to send the filter conditions, you can set a flag at grid scope by providing an input property named <mark style="color:orange;">`onDemandFilter`</mark>.

{% code title="column-filters.component.html" %}

```markup
<dike-grid id="grid-col-filters" height="600px" #grid="dkgGrid"
    [onDemandFilter]="gridProperties.onDemandFilter">
</dike-grid>
```

{% endcode %}

Since we take the flag value from the `gridProperties` object, please open the [Floating Configuration Panel](/#floating-configuration-panel) and mark the checkbox **on-demand Filter** under the **Filtering** section.

![Floating Configuration Panel - Filtering](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F2OtUPdlhx9h1L3Hy5gjK%2Fgrid-structure-row-filter-panel-conf.png?alt=media\&token=8812804b-b5eb-492d-b07e-38078fc75f7f)

Open the [Column Context Menu](/fundamentals/grid-structure/column-context-menu) for a **filterable** column. Next, you will see the **Filter** button. Click on this button when you have finished defining the filter conditions for the column.&#x20;

![Filtering on-demand](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FL5YFQDOwPS0lwW6Oh14l%2Fcolumn-filters-on-demand.gif?alt=media\&token=1cba49b7-7330-4002-973b-a1c07cdf241c)

## Filtering using the API

You can achieve all the previous tasks by using the corresponding API.&#x20;

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

{% tabs %}
{% tab title="column-filters.component.html" %}

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

{% endtab %}

{% tab title="column-filters.component.ts" %}

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

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

```

{% endtab %}
{% endtabs %}

### DikeGrid Column API

Using the **column API,** you can change, at **runtime**, a column definition to be filterable or change its filter conditions.

The following methods belong to the **column API** regarding filtering:

| Method                      | Description                                                                                                   |
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `setColumnFilterablility()` | Use this method to change a column to be **filterable**. You can provide an **initial filter** to be applied. |
| `changeColumnFilter()`      | This method sets the given **filter definition** for the given column.                                        |

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

### DikeGrid Filter API

The following methods comprise the **filter API**:

| Method              | Description                                                                                            |
| ------------------- | ------------------------------------------------------------------------------------------------------ |
| `clearFilter()`     | This method will **remove** all the filter **conditions** from every filterable.                       |
| `isFilterApplied()` | It returns a **boolean value** indicating if the DikeGrid instance has one filter condition, at least. |
| `getFilteredRows()` | It returns all the **rows** that **meet** all the filter conditions **criteria**.                      |

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

To see the API in action, let us define the following UI:

{% tabs %}
{% tab title="column-filters.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)="onColumnAsFilterable()">Surname - filterable
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onInitialFilter()">Email - Initial filter
        </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)="onChangeColumnFilter()">Age - Change column filter
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetFilterables()">Filterables
        </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)="onGetFilteredRows()">Filtered rows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onClearFilter()">Clear filter
        </button>
    </div>
</div>

<dike-grid id="grid-col-filters" height="600px" #grid="dkgGrid">
</dike-grid>

```

{% endtab %}

{% tab title="column-filters.component.ts" %}

```typescript
onColumnAsFilterable(): void {
  // Get the Surname column:
  const surnameColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'lastName');

  // The column exist and is a data column:
  if (!!surnameColumn && isDikeDataColumnDef(surnameColumn)) {
    // Toggle the filterable value:
    this.dikeGrid.columnDef.setColumnFilterability(surnameColumn, !surnameColumn.filterable);
  }
}

onInitialFilter(): void {
  // Get the email column:
  const emailColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'email');

  // The column exist and is a data column:
  if (!!emailColumn && isDikeDataColumnDef(emailColumn)) {
    // Toggle the filterable value and set an initial filter:
    this.dikeGrid.columnDef.setColumnFilterability(emailColumn, !emailColumn.filterable, [{
      condition: ConditionType.CONTAINS, logicalOperator: 'and', value: '@gmail.com'
    } as DikeTextFilter]);
  }
}

onChangeColumnFilter(): void {
  // Get the age column:
  const ageColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'age');

  // The column exist and is a data column:
  if (!!ageColumn && isDikeDataColumnDef(ageColumn)) {
    // Define the new filter:
    const newFilter: DikeNumericFilter[] = [ { condition: ConditionType.EQUALS, logicalOperator: 'or', value1: 23 } ];
    // Merge the existing filter with the new one:
    const finalFilter: DikeNumericFilter[] = !!ageColumn.filter ? [ ...ageColumn.filter, ...newFilter ] : newFilter;
    // Apply the new filter:
    this.dikeGrid.columnDef.changeColumnFilter(ageColumn, finalFilter);
  }
}

onGetFilterables(): void {
  console.log('Filterables: ', this.dikeGrid.filter.filterables);
}

onGetFilteredRows(): void {
  console.log('Filtered rows: ', this.dikeGrid.filter.getFilteredRows());
}

onClearFilter(): void {
  this.dikeGrid.filter.clearFilter();
}

```

{% endtab %}
{% endtabs %}

The previous definition generates the following output:

![Filtering - Using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FbMAOnIX2TnjCrGCrSBPk%2Fcolumn-filters-using-api.png?alt=media\&token=a842ddad-b942-41d9-bd9c-dbc36171cafd)

In the following list, we describe every button action:

1. **Surname - filterable**. Click on this button to make the **Surname** column filterable or not filterable. It toggles the current filterable value.
2. **Email - Initial filter**. Same as before, it toggles the current filterable value. When the column becomes filterable, the given filter is applied. Otherwise, the DikeGrid instance will ignore the given filter.
3. **Age - Change column filter**. Since the **Age** column is filterable, we change its filter, merging the current condition with the new one. If you click more than once, the action will append the new filter condition to the existing ones.
4. **Filterables**. Clicking on this button will print out, to the **dev console,** the current filterables.
5. **Filtered rows**. Clicking on this button will print out, to the **dev console**, all the rows that meet the filter conditions criteria.
6. **Clear filter**. Clicking on this button will **remove** all the filter conditions.

{% hint style="info" %}
When defining a column as **filterable**, the DikeGrid **registers** that column in an internal **Map** structure named ***filterables***. The items of this Map are of type [<mark style="color:green;">`DikeFilterable`</mark>](/reference/interfaces/filtering#dikefilterable-less-than-t-greater-than).
{% endhint %}

## Filter Events

When defining a column as **filterable** or **adding/removing** a filter condition, the DikeGrid instance **emits** the related events.

The filter events are:

| Event                        | Description                                                                                                                                                                    |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `registerFilterableChange`   | It emits when you define a column as **filterable**. Then, the DikeGrid instance will add the column to the internal Map named ***filterables***.                              |
| `deregisterFilterableChange` | It emits when you make a column **not filterable** when it was **filterable**. Then, the DikeGrid instance will remove the column to the internal Map named ***filterables***. |
| `filterChange`               | It emits when the user has **defined/changed** a filter condition.                                                                                                             |
| `clearFilterChange`          | It emits after the DikeGrid instance has **removed** all the filter conditions.                                                                                                |

Let us listen to these events:

{% tabs %}
{% tab title="column-filters.component.html" %}

```markup
<dike-grid id="grid-col-filters" height="600px" #grid="dkgGrid"
    (registerFilterableChange)="onRegisterChange($event)"
    (deregisterFilterableChange)="onDeregisterChange($event)"
    (filterChange)="onFilterChange($event)"
    (clearFilterChange)="onClearFilterChange()">
</dike-grid>
```

{% endtab %}

{% tab title="column-filters.component.ts" %}

```typescript
onRegisterChange(filterable: DikeFilterable<Employee>): void {
  console.log('Register change: ', filterable);
}

onDeregisterChange(filterable: DikeFilterable<Employee>): void {
  console.log('Deregister change: ', filterable);
}

onFilterChange(filterable: DikeFilterable<Employee>): void {
  console.log('Filter change: ', filterable);
}

onClearFilterChange(): void {
  console.log('Clear filter - filterables: ', this.dikeGrid.filter.filterables);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
Please, open your **dev console** to see the output of these events.
{% endhint %}

{% hint style="info" %}
You can also listen to these events from the [<mark style="color:green;">`DikeGridFilter`</mark>](/reference/dkgrid-api/dkgridfilter#events) instance.
{% endhint %}

## Row Grouping and Filtering

When grouping the DikeGrid rows by a column and filtering those rows, you will see the **filter icon** on the right side of the **group** name showing how many data rows for that group have met the filter criteria.

We have **allowed Row-Grouping**, and we have defined the Age column as **groupable**.

{% tabs %}
{% tab title="column-filters.component.html" %}

```markup
<dike-grid id="grid-col-filters" height="600px" #grid="dkgGrid"
    allowRowGrouping
    [allowColumnDragging]="gridProperties.allowColumnDragging">
</dike-grid>
```

{% endtab %}

{% tab title="column-filters.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Age column:
    const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
    ageColumn.order = 4;
    ageColumn.width = 100;
    ageColumn.sortable = true;
    ageColumn.filterable = true;
    ageColumn.groupable = true;
    ageColumn.draggable = true;
    ageColumn.filter = [{
        condition: ConditionType.GREATER_THAN,
        logicalOperator: 'and',
        value1: 50
    }];
    
    //...
}
```

{% endtab %}
{% endtabs %}

![Filtering and Row Grouping](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Ff4HzaT8xp5IT6MQBnp79%2Fcolumn-filters-row-grouping.gif?alt=media\&token=a3dbe12b-facc-4223-91e4-a9b96d8680fb)

If you **group** the DikeGrid rows by a column with filters, the DikeGrid will **ignore** those filters.&#x20;

![Row-Grouping ignores the filters](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FuCTCfNKLcdKqq6qscOHD%2Fcolumn-filters-row-grouping-ignores-filters.gif?alt=media\&token=677a4f76-3c71-4a5f-af15-f648c08221fb)

As you can see in the previous output, the DikeGrid **ignores** the filter when **grouping** the DikeGrid rows by the **Age** column despite defining a filter for this column. As a result, the DikeGrid shows all the values for the **Age** column as **groups**.

## Summary

It is enough to define a column as **filterable** to give the user the ability to filter the DikeGrid rows. The user can add any number of filter conditions. By default, `Text` and `Numeric` filter types have a **debounce time**, but you can change this value or send filters **on-demand**.

You can use the **column API** and the **filter API** to perform filtering tasks. All the filter actions emit their corresponding **event**.

### Complete code for this section.

{% tabs %}
{% tab title="column-filters.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)="onColumnAsFilterable()">Surname - filterable
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onInitialFilter()">Email - Initial filter
        </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)="onChangeColumnFilter()">Age - Change column filter
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetFilterables()">Filterables
        </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)="onGetFilteredRows()">Filtered rows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onClearFilter()">Clear filter
        </button>
    </div>
</div>
<dike-grid id="grid-col-filters" height="600px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    allowRowGrouping
    [allowColumnDragging]="gridProperties.allowColumnDragging"
    [allowSorting]="gridProperties.allowSorting"
    [onDemandFilter]="gridProperties.onDemandFilter"

    (registerFilterableChange)="onRegisterChange($event)"
    (deregisterFilterableChange)="onDeregisterChange($event)"
    (filterChange)="onFilterChange($event)"
    (clearFilterChange)="onClearFilterChange()"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350"
        order="1"
        sortable>
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name"
        order="2">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            filterable
            [filter]="[{ condition: 'startsWith', logicalOperator: 'and', value: 'w' }]">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            sortable>
        </dike-grid-column>
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="110"
        order="3"
        sortable>
    </dike-grid-column>
    
</dike-grid>    
```

{% endtab %}

{% tab title="column-filters.component.ts" %}

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

import { ConditionType, DikeDateColumnDef, DikeFilterable, DikeGridColumnDef, DikeGridComponent, DikeGridDataSourceInput,
    DikeNumericColumnDef, DikeNumericFilter, DikeTextColumnDef, DikeTextFilter, 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: 'column-filters',
  templateUrl: './column-filters.component.html',
  styleUrls: ['./column-filters.component.scss'],

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Age column:
    const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
    ageColumn.order = 4;
    ageColumn.width = 100;
    ageColumn.sortable = true;
    ageColumn.filterable = true;
    ageColumn.groupable = true;
    ageColumn.draggable = true;
    ageColumn.filter = [{
        condition: ConditionType.GREATER_THAN,
        logicalOperator: 'and',
        value1: 50
    }];

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.order = 5;
    hireDateColumn.width = 120;
    hireDateColumn.sortable = true;
    hireDateColumn.filterable = true;

    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.order = 6;
    emailColumn.width = 250;
    emailColumn.sortable = true;

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns([ ageColumn, hireDateColumn, emailColumn ]);
  }

  ngOnInit(): void {
    // Get 1000 entries from the REST API:
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

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

  onColumnAsFilterable(): void {
    // Get the Surname column:
    const surnameColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'lastName');

    // The column exist and is a data column:
    if (!!surnameColumn && isDikeDataColumnDef(surnameColumn)) {
      // Toggle the filterable value:
      this.dikeGrid.columnDef.setColumnFilterability(surnameColumn, !surnameColumn.filterable);
    }
  }

  onInitialFilter(): void {
    // Get the email column:
    const emailColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'email');

    // The column exist and is a data column:
    if (!!emailColumn && isDikeDataColumnDef(emailColumn)) {
      // Toggle the filterable value and set an initial filter:
      this.dikeGrid.columnDef.setColumnFilterability(emailColumn, !emailColumn.filterable, [{
        condition: ConditionType.CONTAINS, logicalOperator: 'and', value: '@gmail.com'
      } as DikeTextFilter]);
    }
  }

  onChangeColumnFilter(): void {
    // Get the age column:
    const ageColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'age');

    // The column exist and is a data column:
    if (!!ageColumn && isDikeDataColumnDef(ageColumn)) {
      // Define the new filter:
      const newFilter: DikeNumericFilter[] = [ { condition: ConditionType.EQUALS, logicalOperator: 'or', value1: 23 } ];
      // Merge the existing filter with the new one:
      const finalFilter: DikeNumericFilter[] = !!ageColumn.filter ? [ ...ageColumn.filter, ...newFilter ] : newFilter;
      // Apply the new filter:
      this.dikeGrid.columnDef.changeColumnFilter(ageColumn, finalFilter);
    }
  }

  onGetFilterables(): void {
    console.log('Filterables: ', this.dikeGrid.filter.filterables);
  }

  onGetFilteredRows(): void {
    console.log('Filtered rows: ', this.dikeGrid.filter.getFilteredRows());
  }

  onClearFilter(): void {
    this.dikeGrid.filter.clearFilter();
  }

  onRegisterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Register change: ', filterable);
  }

  onDeregisterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Deregister change: ', filterable);
  }

  onFilterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Filter change: ', filterable);
  }

  onClearFilterChange(): void {
    console.log('Clear filter - filterables: ', this.dikeGrid.filter.filterables);
  }

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

```

{% endtab %}

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

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

import { DikeDataGridModule, NUMERIC_FILTER_DEBOUNCE_TIME,
    CUSTOM_ROW_TEXT_FILTER_CONDITIONS, CustomRowTextCaseFilterCondition, ConditionType,
    CUSTOM_TEXT_FILTER_CONDITIONS, CustomTextCaseFilterCondition,
    DikeDataColumnDef, DikeTextFilter } from '@dikesoft/angular-data-grid';

import { SharedModule } from 'app/shared/shared.module';
import { Employee } from 'app/mock-api/common/employees/data.model';
import { filteringRoutes } from 'app/modules/admin/filtering/filtering.routing';

import { ColumnFiltersComponent } from './column-filters/column-filters.component';
import { FilterTypesComponent } from './filter-types/filter-types.component';
import { InLineFiltersComponent } from './in-line-filters/in-line-filters.component';

@NgModule({
  declarations: [
    ColumnFiltersComponent,
    FilterTypesComponent,
    InLineFiltersComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild(filteringRoutes),

    SharedModule,
    DikeDataGridModule
  ],
  providers: [
    { provide: NUMERIC_FILTER_DEBOUNCE_TIME, useValue: 300 },
    {
      provide: CUSTOM_ROW_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomRowTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition<Employee>()
          .addExistingCondition(ConditionType.ENDS_WITH)
    },
    {
      provide: CUSTOM_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition()
          .addCondition({
            text: 'Custom Lower Case',
            value: 'global-customLowerCaseText',
            eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextFilter): boolean =>
              dataColumnDef.getValue(entry).toLowerCase().includes(values.value)
          })
    }
  ]
})
export class FilteringModule { }

```

{% endtab %}
{% endtabs %}


# Filter types

This section describes the types of filters and how you can add or overwrite conditions for each of them.

In general, filters depend on the column type where the filter will apply. Therefore, the filter types are `Text`, `Numeric`, `Date`, and `Binary`.

There is another type of filter named `Multiple Selection` filter. But this type of filter can apply to `Text` and `Numeric` filters.

{% hint style="info" %}
See the column types in the [Columns Definitions](/columns/column-definitions#column-types) section.
{% endhint %}

## Live example

{% hint style="success" %}
[Filter types](https://demos.dikesoft.com/dk-grid/filtering/filter-types) live example.
{% endhint %}

## Filter conditions

The following table shows the existing filter types and the conditions that apply for each of them:

| Filter type          | Conditions                                                                                                  |
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
| `Text`               | Empty, Equals, Not Equals, Contains, Not Contains, Starts With, and Ends With.                              |
| `Numeric`            | Empty, Equals, Not Equals, Less Than, Less Than or Equals, Greater Than, Greater Than Or Equals, and Range. |
| `Date`               | Empty, Equals, Not Equals, Less Than, Less Than or Equals, Greater Than, Greater Than Or Equals, and Range. |
| `Binary`             | Empty, Equals, and Not Equals.                                                                              |
| `Multiple Selection` | Empty, Equals, and Not Equals.                                                                              |

## Customizing filter conditions

Since filter execution comes down to evaluating every filter condition against the data set, you will see how to implement your filter conditions per column or at the grid scope.

{% hint style="info" %}
All **custom** instances do not have any defined condition. You must add or implement every filter condition.
{% endhint %}

{% hint style="success" %}
When defining a custom condition, you must implement an object of type [<mark style="color:green;">`Condition<T, R, V>`</mark>](/reference/interfaces/filtering#condition-less-than-t-r-v-greater-than).
{% endhint %}

### Text Filter

You must provide an instance of type [<mark style="color:green;">`CustomTextCaseFilterCondition`</mark>](/reference/classes/filtering#customtextcasefiltercondition-less-than-t-greater-than) when defining a column.

{% tabs %}
{% tab title="filter-types.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid">
    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name"
        order="2">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            filterable
            [customFilterConditions]="nameConditions">
        </dike-grid-column>

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

</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

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

    encapsulation: ViewEncapsulation.None,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class FilterTypesComponent implements OnInit, OnDestroy {
  nameConditions: CustomTextCaseFilterCondition<Employee>;

  ngOnInit(): void {
    // We only add Contains and Not Contains filter conditions to the Name column:
    this.nameConditions = new CustomTextCaseFilterCondition<Employee>()
      .addExistingCondition(ConditionType.CONTAINS)
      .addExistingCondition(ConditionType.NOT_CONTAINS)
      // And we provide our Starts With implementation:
      .addCondition({
        text: 'My Starts With',
        value: ConditionType.STARTS_WITH,
        eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextCaseFilter) =>
          entry.firstName.toLocaleLowerCase().startsWith(values.value.toLowerCase())
      });
  }
}
```

{% endtab %}
{% endtabs %}

We added only two conditions to the **Name** column: **Contains** and **Not Contains**. We also added our custom implementation of the **StartsWith** condition.

![Text Filter - Custom conditions](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FvBWk8zOcOUQtbf5HMxZ3%2Ffilter-types-text-custom-conditions.png?alt=media\&token=45cbfc29-d5bf-4163-8d60-825256ebe9a4)

{% hint style="warning" %}
The <mark style="color:orange;">`customFilterConditions`</mark> property of the [<mark style="color:green;">`DikeGridColumnComponent`</mark>](/reference/components/dkgridcolumncomponent#properties) is of type [<mark style="color:green;">`CustomFilterConditionInstance`</mark>](/reference/type-aliases/filtering#customfilterconditioninstance-less-than-t-greater-than). Therefore, be aware of assigning the correct custom instance.
{% endhint %}

### Numeric Filter

You must provide a [<mark style="color:green;">`CustomNumericFilterCondition`</mark>](/reference/classes/filtering#customnumericfiltercondition-less-than-t-greater-than) instance for numeric types when defining a column.

For the **Age** column, let us define a filter that gives us all the numbers outside a close interval.

{% tabs %}
{% tab title="filter-types.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid"
    (gridColumnDefInstance)="onColumnDefInstance($event)">
</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
  // Define the Age column:
  const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
  ageColumn.order = 3;
  ageColumn.width = 100;
  ageColumn.filterable = true;
  ageColumn.sortable = true;
  ageColumn.customFilterConditions = new CustomNumericFilterCondition<Employee>()
    .addCondition({
        text: 'Not In Close Interval [value1, value2]',
        value: 'not-in-range-interval',
        eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, number>, values?: DikeNumericFilter) =>
          !(entry.age >= values.value1 && entry.age <= values.value2)
        });

  //...
}
```

{% endtab %}
{% endtabs %}

Notice how we have added the word ***range*** in the condition value (**not-in-range-interval**). If you omit the *range* word, the UI will not show the second textbox for typing a numeric value.

![Numeric Filter - Custom conditions](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FLOvEFDKzxxLwGdXNf3N3%2Ffilter-types-numeric-custom-conditions.png?alt=media\&token=647ef994-e51c-49ea-a79c-e1e9bdb84251)

### Date Filter

You must provide a [<mark style="color:green;">`CustomDateFilterCondition`</mark>](/reference/classes/filtering#customdatefiltercondition-less-than-t-greater-than) instance for <mark style="color:green;">`Date`</mark> types when defining a column.

{% hint style="info" %}
A Date Filter has the same conditions as a Numeric Filter.
{% endhint %}

Let us define some custom conditions for the **Hire Date** column:

{% tabs %}
{% tab title="filter-types.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid"
    (gridColumnDefInstance)="onColumnDefInstance($event)">
</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
  // Define the Hire Date column:
  const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
  hireDateColumn.order = 5;
  hireDateColumn.width = 120;
  hireDateColumn.sortable = true;
  hireDateColumn.filterable = true;
  hireDateColumn.customFilterConditions = new CustomDateFilterCondition<Employee>()
    .addExistingCondition(ConditionType.GREATER_THAN)
    .addExistingCondition(ConditionType.LESS_THAN)
    .addCondition({
        text: 'Close Interval',
        value: 'close-range-interval',
        eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string | number | Date>, values?: DikeDateFilter) =>
          entry.hireDate >= values.value1 && entry.hireDate <= values.value2
    });
    
  //...
}
```

{% endtab %}
{% endtabs %}

We have added two existing conditions in the previous example: **Greater Than** and **Less Than**. We also defined a custom condition: **Close Interval**. Same as before, we added the word ***range*** to the value of the custom condition.

![Date Filter - Custom conditions](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FGnB7tj7CD9dSkihZKUOz%2Ffilter-types-date-custom-conditions.png?alt=media\&token=41c93457-c685-480c-8a35-5db6753e541e)

{% hint style="warning" %}
As you can see, the DikeGrid uses the native <mark style="color:green;">`Date`</mark> type. So do not forget to import the <mark style="color:green;">`MatNativeDateModule`</mark> or a custom implementation instead.
{% endhint %}

### Binary Filter

With the Binary Filter, the user must select between two options only. Furthermore, those options are mutually exclusive by definition.

{% hint style="info" %}
For filtering, the DikeGrid casts Binary Filters values to string values.
{% endhint %}

If you only define a `Binary` column as **filterable**, you will see the filters options as shown in the following screenshot:

![Binary Filter - Gender column](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FvRhaGcWw5s4cTVa2jAYr%2Ffilter-types-binary-filter.png?alt=media\&token=c7b28af8-45f3-4634-8c76-8953d9d5d5b4)

Let us define options according to our values in our data set for the **Gender** column. For `Binary` columns, we have to provide a [<mark style="color:green;">`CustomBinaryFilterCondition`</mark>](/reference/classes/filtering#custombinaryfiltercondition-less-than-t-greater-than) instance to add or overwrite conditions.

{% tabs %}
{% tab title="filter-types.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid"
    (gridColumnDefInstance)="onColumnDefInstance($event)">
</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
  // Define the Gender column:
  const genderColumn = new DikeBinaryColumnDef<Employee>('gender', 'Gender');
  genderColumn.order = 4;
  genderColumn.width = 110;
  genderColumn.sortable = true;
  genderColumn.filterable = true;
  genderColumn.customFilterConditions = new CustomBinaryFilterCondition<Employee>()
    .addExistingCondition(ConditionType.EQUALS)
    .addExistingCondition(ConditionType.NOT_EQUALS);
  
  // Then, add the visible options:
  genderColumn.customFilterConditions.options = [ { label: 'Female', value: 'female' }, { label: 'Male', value: 'male', selected: true } ];
}
```

{% endtab %}
{% endtabs %}

We have defined the following features with the previous code snippet:

1. We added **Equals** and **Not Equals** conditions. If you try adding a **non-valid** condition, the DikeGrid will only **add** **valid** filter conditions.
2. We have defined our **labels** and **values** according to the **Gender** column values.
3. Notice how we have added an **initial filter** for the **Gender** column. We tell our DikeGrid that filters the data set with a male value setting the `selected` property to true.

The previous filter definition generates the following output:

![Binary Filter - Custom conditions](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FNI0f7xY9M7wpFqA8q5MI%2Ffilter-types-binary-custom-conditions.png?alt=media\&token=e19693cb-59f6-4d1c-ad5c-58bfe6b5096f)

{% hint style="info" %}
Options for Binary custom filters are of type [<mark style="color:green;">`DikeBinarySelectionModel`</mark>](/reference/interfaces/filtering#dikebinaryselectionmodel), and you can only provide **two** options. If you offer more than two options, DikeGrid will ignore them.
{% endhint %}

{% hint style="danger" %}
If you try to add a condition different from **Equals**, **Not Equal**, or **Empty**, the DikeGrid will throw an error. You can only overwrite the mentioned conditions.
{% endhint %}

### Multiple Selection

In some cases, you want the user to select several options but a fixed number of options.

Consider the following definitions in the evaluation of the conditions:

| Condition  | Evaluation                                                                                                                                                                                                                |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Equals     | The DikeGrid evaluates the selected options with the **OR** logical operator.                                                                                                                                             |
| Not Equals | The DikeGrid evaluates the selected options with the **AND** logical operator. Then, it takes the **complement** of the result set.                                                                                       |
| Empty      | If you add the Empty condition, it will appear as a checkbox indicating if the filtering operation **includes** or **excludes** the empty values. The latter depends on the Equals or Not Equals selection, respectively. |

#### Multiple Text Filter

For Multiple Text Filter conditions, you must provide a [<mark style="color:green;">`CustomMultipleTextFilterCondition`</mark>](/reference/classes/filtering#custommultipletextfiltercondition-less-than-t-greater-than) instance to a `Text` column type.

Let us define a Multiple Text Filter for the **Surname** column:

{% tabs %}
{% tab title="filter-types.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid">
    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name"
        order="2">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            filterable
            [customFilterConditions]="nameConditions">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            filterable
            [customFilterConditions]="surnameConditions"
            sortable>
        </dike-grid-column>
    </dike-grid-column>

</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

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

    encapsulation: ViewEncapsulation.None,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class FilterTypesComponent implements OnInit, OnDestroy {

  surnameConditions: CustomMultipleTextFilterCondition<Employee>;

  ngOnInit(): void {
    // We add all the valid conditions for Multiple Selection filters:
    this.surnameConditions = new CustomMultipleTextFilterCondition<Employee>()
      .addExistingCondition(ConditionType.EMPTY)
      .addExistingCondition(ConditionType.EQUALS)
      .addExistingCondition(ConditionType.NOT_EQUALS);
    
    // And we have added several options:
    this.surnameConditions.options = [
      { label: 'Abbott', value: 'Abbott' },
      { label: 'Adams', value: 'Adams' },
      { label: 'Bahringer', value: 'Bahringer' },
      { label: 'Balistreri', value: 'Balistreri' },
      { label: 'Beier', value: 'Beier' },
      { label: 'Bernier', value: 'Bernier' },
      { label: 'Dare', value: 'Dare' },
      { label: 'Gutkowski', value: 'Gutkowski' },
      { label: 'Herman', value: 'Herman' },
      { label: 'Lockman', value: 'Lockman' },
      { label: 'Marvin', value: 'Marvin' },
      { label: 'Parker', value: 'Parker' },
      { label: 'Reynolds', value: 'Reynolds' },
      { label: 'Ritchie', value: 'Ritchie' },
      { label: 'Schmidt', value: 'Schmidt' },
      { label: 'Thompson', value: 'Thompson' },
      { label: 'Walker', value: 'Walker' },
      { label: 'Williamson', value: 'Williamson' },
      { label: 'Zulauf', value: 'Zulauf' }
    ];
  }
}
```

{% endtab %}
{% endtabs %}

With the previous code snippet:

1. We have added the **Empty**, **Equals**, and **Not Equals** filter conditions.
2. We have defined several options from which the user can select.
3. We could have provided an initial filter **selecting** more than one option.

![Multiple Text Filter - Surname column](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FOwL0iNCrU7U5OhwAh2o6%2Ffilter-types-multiple-text-filter.png?alt=media\&token=f964ae8d-a900-457e-b81a-6220c958248b)

{% hint style="info" %}
You can provide any number of options for **Multiple Text Filter**. The array you provide is of type [<mark style="color:green;">`DikeTextSelectionModel`</mark>](/reference/interfaces/filtering#diketextselectionmodel).
{% endhint %}

#### Multiple Numeric Filter

For Multiple Numeric Filter conditions, you must provide a [<mark style="color:green;">`CustomMultipleNumericFilterCondition`</mark>](/reference/classes/filtering#custommultiplenumericfiltercondition-less-than-t-greater-than) instance to a `Numeric` column type.

Let us define a Multiple Text Filter for the **Performance** column:

{% tabs %}
{% tab title="filter-types.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid"
    (gridColumnDefInstance)="onColumnDefInstance($event)">
</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
  // Define the Performance column:
  const performanceColumn = new DikeNumericColumnDef<Employee>('performance', 'Performance');
  performanceColumn.order = 7;
  performanceColumn.width = 120;
  performanceColumn.sortable = true;
  performanceColumn.filterable = true;
  performanceColumn.getValue = (entry: Employee): number => Math.round(entry.performance);
  // Define the filter as Multiple Selection filter:
  performanceColumn.customFilterConditions = new CustomMultipleNumericFilterCondition<Employee>()
      .addExistingCondition(ConditionType.EQUALS)
      .addExistingCondition(ConditionType.NOT_EQUALS);
  
  // Add a fixed number of options:
  performanceColumn.customFilterConditions.options = [
      { label: 'Five', value: 5 },
      { label: 'Four', value: 4 },
      { label: 'Three', value: 3 },
      { label: 'Two', value: 2 },
      { label: 'One', value: 1 },
  ];
  
  //...
}
```

{% endtab %}
{% endtabs %}

With the previous code snippet:

1. We have added **Equals** and **Not Equals** conditions.
2. We added five options to the custom filter.
3. **Important**. Notice how we define the getter function rounding the performance value to close the options to an integer value.

![Multiple Numeric Filter - Performance column](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fo1RVGER7PW2oSW6bdz6r%2Ffilter-types-multiple-numeric-filter.png?alt=media\&token=fa4e9a5f-08f4-406a-8fb8-b2fd84ad3fa4)

{% hint style="info" %}
You can provide any number of options for **Multiple Numeric Filter**. The array you provide is of type [<mark style="color:green;">`DikeNumericSelectionModel`</mark>](/reference/interfaces/filtering#dikenumericselectionmodel).
{% endhint %}

## Customizing conditions using the API

You can change the column filter conditions at **runtime**.

| Method                     | Description                                                                                                                                                                                                                                                                                                                                                                                  |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setColumnFilterability()` | You can provide an instance of type [<mark style="color:green;">`CustomFilterConditionInstance`</mark>](/reference/type-aliases/filtering#customfilterconditioninstance-less-than-t-greater-than). Be aware of passing the correct instance depending on the column type. The DikeGrid will take the provided instance if the **filterable** flag is <mark style="color:red;">`true`</mark>. |

## Customization at the grid level

The previous customizations apply at the column level. In addition, you can change the definition at the grid level.

To change conditions at grid scope, you must provide the corresponding custom instance through an input property named <mark style="color:orange;">`gridCustomFilterConditions`</mark>.

{% hint style="info" %}
The <mark style="color:orange;">`gridCustomFilterConditions`</mark> property is of type [<mark style="color:green;">`DikeGridCustomFilterConditions`</mark>](/reference/type-aliases/filtering#dikegridcustomfilterconditions-less-than-t-greater-than). You **can not change** this property at **runtime**.
{% endhint %}

Let us provide a custom condition for `Text` types.

{% tabs %}
{% tab title="filter-types.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid"
    [gridCustomFilterConditions]="gridCustomConditions">
</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

```typescript
ngOnInit(): void {
  //...
  // Defining filter conditions at DikeGrid instance level:
  this.gridCustomConditions = {
    customTextFilterConditions: new CustomTextCaseFilterCondition<Employee>()
      .addExistingCondition(ConditionType.CONTAINS)
      .addCondition({
          text: 'Grid Custom Upper Case',
          value: 'grid-customUpperCaseText',
          eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextFilter): boolean =>
            dataColumnDef.getValue(entry).toUpperCase().includes(values.value)
      })
  };
}
```

{% endtab %}
{% endtabs %}

We have defined a new custom condition for `Text` types named ***grid-customUpperCaseText***. This condition will apply to the **Email** column only because we have not specified any custom condition for that column. See the following screenshot.

![Customization at the grid level - Text types](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkIqWMBLPglEp2Y67GSZz%2Ffilter-types-grid-scope.png?alt=media\&token=32ff9c2b-e0a0-4592-9f3b-f446ff4bec8a)

{% hint style="info" %}
It is essential to notice that when you define a new custom condition at the grid level, the DikeGrid will **add** this condition to the existing ones.
{% endhint %}

## Customization by providing an Injection Token

The lowest precedence to define a new condition is by providing an Injection Token.

| Injection Token                                                     | Custom instance                                                                                                                                          |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:blue;">`CUSTOM_TEXT_FILTER_CONDITIONS`</mark>    | [<mark style="color:green;">`CustomTextCaseFilterCondition`</mark>](/reference/classes/filtering#customtextcasefiltercondition-less-than-t-greater-than) |
| <mark style="color:blue;">`CUSTOM_NUMERIC_FILTER_CONDITIONS`</mark> | [<mark style="color:green;">`CustomNumericFilterCondition`</mark>](/reference/classes/filtering#customnumericfiltercondition-less-than-t-greater-than)   |
| <mark style="color:blue;">`CUSTOM_DATE_FILTER_CONDITIONS`</mark>    | [<mark style="color:green;">`CustomDateFilterCondition`</mark>](/reference/classes/filtering#customdatefiltercondition-less-than-t-greater-than)         |
| <mark style="color:blue;">`CUSTOM_BINARY_FILTER_CONDITIONS`</mark>  | [<mark style="color:green;">`CustomBinaryFilterCondition`</mark>](/reference/classes/filtering#custombinaryfiltercondition-less-than-t-greater-than)     |

Let us define a custom condition for `Text` types.

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

```typescript
@NgModule({
   providers: [
    {
      provide: CUSTOM_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition()
          .addCondition({
            text: 'Global Custom Lower Case',
            value: 'global-customLowerCaseText',
            eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextFilter): boolean =>
              dataColumnDef.getValue(entry).toLowerCase().includes(values.value)
          })
    }
  ]
})
export class FilteringModule { }

```

{% endcode %}

We have defined a new custom condition for `Text` types named ***global-customLowerCaseText***. This condition will apply to the **Email** column only because we have not specified any custom condition for that column. See the following screenshot.

![Customization at the module level - Text types](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FIGb6hADnqAXP9rh6dYoZ%2Ffilter-types-module-scope.png?alt=media\&token=0ac6a51b-4a64-40d0-be0b-d5c65821efb5)

{% hint style="info" %}
It is essential to notice that when you define a new custom condition by providing an Injection Token, the DikeGrid will **add** this condition to the existing ones.
{% endhint %}

## Empty Values

When the DikeGrid instance filters the rows, it considers the following values as empties:

| Filter type         | Value                                                                       |
| ------------------- | --------------------------------------------------------------------------- |
| `Text` and `Binary` | `null`, `undefined`, and empty <mark style="color:green;">`strings`</mark>. |
| `Numeric`           | `null`, `undefined`, and <mark style="color:green;">`NaN`</mark> values.    |
| `Date`              | `null`, `undefined`, and <mark style="color:red;">`Invalid Dates`</mark>.   |

{% hint style="info" %}
We recommend you define the **getter function** for the columns you define. For further details, see the [Column Definitions](/columns/column-definitions#retrieving-and-updating-a-field-value) section.
{% endhint %}

## Summary

Filters depend on the column type where the filter will apply. Apart from the column type a filter applies, there is one more type: **Multiple Selection** Filter, an extension of Text and Numeric filter types.

Since filter execution comes down to evaluating every filter condition, you can **add** or **overwrite** conditions.

### Complete code for this section

{% tabs %}
{% tab title="filter-types.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)="onGetFilterables()">Filterables
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onGetFilteredRows()">Filtered rows
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onClearFilter()">Clear filter
    </button>
</div>

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

    [allowSorting]="gridProperties.allowSorting"
    [allowPagination]="gridProperties.allowPagination"

    (registerFilterableChange)="onRegisterChange($event)"
    (filterChange)="onFilterChange($event)"
    [gridCustomFilterConditions]="gridCustomConditions"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">

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

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            filterable
            [customFilterConditions]="nameConditions">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            filterable
            [customFilterConditions]="surnameConditions"
            sortable>
        </dike-grid-column>
    </dike-grid-column>
</dike-grid>
```

{% endtab %}

{% tab title="filter-types.component.ts" %}

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

import { CustomTextCaseFilterCondition, DikeDateColumnDef, DikeFilterable, DikeGridColumnDef, DikeGridComponent,
    DikeGridDataSourceInput, DikeNumericColumnDef, DikeTextColumnDef, ConditionType, CustomNumericFilterCondition,
    DikeDataColumnDef, DikeNumericFilter, CustomDateFilterCondition, DikeDateFilter, DikeTextCaseFilter,
    DikeBinaryColumnDef, CustomBinaryFilterCondition, CustomMultipleTextFilterCondition, CustomMultipleNumericFilterCondition,
    DikeGridCustomFilterConditions
} 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: 'filter-types',
    templateUrl: './filter-types.component.html',
    styleUrls: ['./filter-types.component.scss'],

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;
  nameConditions: CustomTextCaseFilterCondition<Employee>;
  surnameConditions: CustomMultipleTextFilterCondition<Employee>;
  gridCustomConditions: DikeGridCustomFilterConditions<Employee>;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Age column:
    const ageColumn = new DikeNumericColumnDef<Employee>('age', 'Age');
    ageColumn.order = 3;
    ageColumn.width = 100;
    ageColumn.filterable = true;
    ageColumn.sortable = true;
    ageColumn.customFilterConditions = new CustomNumericFilterCondition<Employee>()
      .addCondition({
        text: 'Not In Close Interval [value1, value2]',
        value: 'not-in-range-interval',
        eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, number>, values?: DikeNumericFilter) =>
          !(entry.age >= values.value1 && entry.age <= values.value2)
      });

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.order = 5;
    hireDateColumn.width = 120;
    hireDateColumn.sortable = true;
    hireDateColumn.filterable = true;
    hireDateColumn.customFilterConditions = new CustomDateFilterCondition<Employee>()
      .addExistingCondition(ConditionType.GREATER_THAN)
      .addExistingCondition(ConditionType.LESS_THAN)
      .addCondition({
        text: 'Close Interval',
        value: 'close-range-interval',
        eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string | number | Date>, values?: DikeDateFilter) =>
          entry.hireDate >= values.value1 && entry.hireDate <= values.value2
      });

    // Define the Gender column:
    const genderColumn = new DikeBinaryColumnDef<Employee>('gender', 'Gender');
    genderColumn.order = 4;
    genderColumn.width = 110;
    genderColumn.sortable = true;
    genderColumn.filterable = true;
    genderColumn.customFilterConditions = new CustomBinaryFilterCondition<Employee>()
      .addExistingCondition(ConditionType.EQUALS)
      .addExistingCondition(ConditionType.NOT_EQUALS);
    // Then, add the visible options:
    genderColumn.customFilterConditions.options = [ { label: 'Female', value: 'female' }, { label: 'Male', value: 'male', selected: true } ];

    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.order = 6;
    emailColumn.width = 250;
    emailColumn.sortable = true;
    emailColumn.filterable = true;

    // Define the Performance column:
    const performanceColumn = new DikeNumericColumnDef<Employee>('performance', 'Performance');
    performanceColumn.order = 7;
    performanceColumn.width = 120;
    performanceColumn.sortable = true;
    performanceColumn.filterable = true;
    performanceColumn.getValue = (entry: Employee): number => Math.round(entry.performance);
    // Define the filter as Multiple Selection filter:
    performanceColumn.customFilterConditions = new CustomMultipleNumericFilterCondition<Employee>()
      .addExistingCondition(ConditionType.EQUALS)
      .addExistingCondition(ConditionType.NOT_EQUALS);
    // Add a fixed number of options:
    performanceColumn.customFilterConditions.options = [
      { label: 'Five', value: 5 },
      { label: 'Four', value: 4 },
      { label: 'Three', value: 3 },
      { label: 'Two', value: 2 },
      { label: 'One', value: 1 },
    ];

    // Then, add the colums to the DikeGridComponent instance:
    columnDef.addColumns([ ageColumn, hireDateColumn, genderColumn, emailColumn, performanceColumn ]);
  }

  ngOnInit(): void {
    // Get 1000 entries from the REST API:
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();

    // We only add Contains and Not Contains filter conditions to the Name column:
    this.nameConditions = new CustomTextCaseFilterCondition<Employee>()
      .addExistingCondition(ConditionType.CONTAINS)
      .addExistingCondition(ConditionType.NOT_CONTAINS)
      // And we provide our Starts With implementation:
      .addCondition({
        text: 'My Starts With',
        value: ConditionType.STARTS_WITH,
        eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextCaseFilter) =>
          entry.firstName.toLocaleLowerCase().startsWith(values.value.toLowerCase())
      });

    // We add all the valid conditions for Multiple Selection filters:
    this.surnameConditions = new CustomMultipleTextFilterCondition<Employee>()
      .addExistingCondition(ConditionType.EMPTY)
      .addExistingCondition(ConditionType.EQUALS)
      .addExistingCondition(ConditionType.NOT_EQUALS);
    // And we have added several options:
    this.surnameConditions.options = [
      { label: 'Abbott', value: 'Abbott' },
      { label: 'Adams', value: 'Adams' },
      { label: 'Bahringer', value: 'Bahringer' },
      { label: 'Balistreri', value: 'Balistreri' },
      { label: 'Beier', value: 'Beier' },
      { label: 'Bernier', value: 'Bernier' },
      { label: 'Dare', value: 'Dare' },
      { label: 'Gutkowski', value: 'Gutkowski' },
      { label: 'Herman', value: 'Herman' },
      { label: 'Lockman', value: 'Lockman' },
      { label: 'Marvin', value: 'Marvin' },
      { label: 'Parker', value: 'Parker' },
      { label: 'Reynolds', value: 'Reynolds' },
      { label: 'Ritchie', value: 'Ritchie' },
      { label: 'Schmidt', value: 'Schmidt' },
      { label: 'Thompson', value: 'Thompson' },
      { label: 'Walker', value: 'Walker' },
      { label: 'Williamson', value: 'Williamson' },
      { label: 'Zulauf', value: 'Zulauf' }
    ];
    
    // Defining filter conditions at DikeGrid instance level:
    this.gridCustomConditions = {
      customTextFilterConditions: new CustomTextCaseFilterCondition<Employee>()
        .addExistingCondition(ConditionType.CONTAINS)
        .addCondition({
          text: 'Grid Custom Upper Case',
          value: 'grid-customUpperCaseText',
          eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextCaseFilter): boolean =>
            dataColumnDef.getValue(entry).toUpperCase().includes(values.value)
        })
    };
  }

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

  onGetFilterables(): void {
    console.log('Filterables: ', this.dikeGrid.filter.filterables);
  }

  onGetFilteredRows(): void {
    console.log('Filtered rows: ', this.dikeGrid.filter.getFilteredRows());
  }

  onClearFilter(): void {
    this.dikeGrid.filter.clearFilter();
  }

  onRegisterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Register change: ', filterable);
  }

  onFilterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Filter change: ', filterable);
  }

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

```

{% endtab %}

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

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

import { DikeDataGridModule, NUMERIC_FILTER_DEBOUNCE_TIME,
    CUSTOM_ROW_TEXT_FILTER_CONDITIONS, CustomRowTextCaseFilterCondition, ConditionType,
    CUSTOM_TEXT_FILTER_CONDITIONS, CustomTextCaseFilterCondition,
    DikeDataColumnDef, DikeTextFilter 
} from '@dikesoft/angular-data-grid';

import { SharedModule } from 'app/shared/shared.module';
import { Employee } from 'app/mock-api/common/employees/data.model';
import { filteringRoutes } from 'app/modules/admin/filtering/filtering.routing';

import { ColumnFiltersComponent } from './column-filters/column-filters.component';
import { FilterTypesComponent } from './filter-types/filter-types.component';
import { InLineFiltersComponent } from './in-line-filters/in-line-filters.component';

@NgModule({
  declarations: [
    ColumnFiltersComponent,
    FilterTypesComponent,
    InLineFiltersComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild(filteringRoutes),

    SharedModule,
    DikeDataGridModule
  ],
  providers: [
    { provide: NUMERIC_FILTER_DEBOUNCE_TIME, useValue: 300 },
    {
      provide: CUSTOM_ROW_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomRowTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition<Employee>()
          .addExistingCondition(ConditionType.ENDS_WITH)
    },
    {
      provide: CUSTOM_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition()
          .addCondition({
            text: 'Global Custom Lower Case',
            value: 'global-customLowerCaseText',
            eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextFilter): boolean =>
              dataColumnDef.getValue(entry).toLowerCase().includes(values.value)
          })
    }
  ]
})
export class FilteringModule { }

```

{% endtab %}
{% endtabs %}


# In-line Filters

This section describes how you can enable filtering for all the displayed columns.

## Live example

{% hint style="success" %}
[In-line Filters](https://demos.dikesoft.com/dk-grid/filtering/in-line-filters) live example.
{% endhint %}

## Enabling In-line Filters

To enable the In-line Filters, you must provide an input property named <mark style="color:orange;">`allowRowFiltering`</mark> at grid scope.

{% code title="in-line-filters.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid"
    [allowRowFiltering]="gridProperties.allowRowFilter">
</dike-grid>
```

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) and click on the **Row Filter** checkbox of the **Filtering** group.

![Floating Configuration Panel - Filtering](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F2OtUPdlhx9h1L3Hy5gjK%2Fgrid-structure-row-filter-panel-conf.png?alt=media\&token=8812804b-b5eb-492d-b07e-38078fc75f7f)

{% hint style="info" %}
You do not have to define every column as filterable to enable the In-line filters.
{% endhint %}

![Enabling In-line Filters](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FLYMRHA8PxSiYZ7Gcc0t1%2Fin-line-filters-enabling.png?alt=media\&token=ff64bd48-1b4f-4efd-bd1d-f2fbf85d1d2f)

As you can see, the DikeGrid shows a textbox for every defined column. Thus, In-line filters are the **quickest** and **easiest** way to enable filtering.

When you enable the In-line Filters, the DikeGrid registers them automatically, appending the suffix ***row-filter*** to the column id.

![In-line Filters registration](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FOYl9Fcbh6dWnByZ3bNEF%2Fin-line-filters-register.png?alt=media\&token=50582501-3693-47e6-816a-fade69388af1)

When you hide In-line Filters, the DikeGrid removes them automatically, as well.

![In-line Filters deregistration](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fxt5Xg5Yhjkai1P8SL1g3%2Fin-line-filters-deregister.png?alt=media\&token=21f44d9a-b138-4a0f-bc09-298fd5c94d27)

{% hint style="success" %}
Open the **dev console** to see the `registerFilterableChange` and `deregisterFilterableChange` events output.
{% endhint %}

## Customizing the In-line Filters row

You can change the row's **height** where the DikeGrid shows the In-line Filters.

{% hint style="info" %}
For further details, see the [Grid Structure - Row Filter](/fundamentals/grid-structure/row-filter#changing-the-row-filter-row-height) section.
{% endhint %}

## In-line Filters conditions

By definition, In-line Filters only have **one filter condition**. The following table shows the filter type and its corresponding default condition:

| Filter type         | Default condition |
| ------------------- | ----------------- |
| `Text` and `Binary` | Contains          |
| `Numeric`           | Equals            |
| `Date`              | Equals            |

{% hint style="info" %}
Be aware that **Binary** Filters become **Text** Filters.
{% endhint %}

## Customizing conditions

You can change the condition of the in-line Filters in two ways: by providing an **Injection Token** or for a specific **grid** instance.

### Customization by providing an Injection Token

To change the default condition, you must provide the corresponding custom instance to the following Injection Tokens:

| Injection Token                                                         | Custom instance                                                                                                                                                |
| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:blue;">`CUSTOM_ROW_TEXT_FILTER_CONDITIONS`</mark>    | [<mark style="color:green;">`CustomRowTextCaseFilterCondition`</mark>](/reference/classes/filtering#customrowtextcasefiltercondition-less-than-t-greater-than) |
| <mark style="color:blue;">`CUSTOM_ROW_NUMERIC_FILTER_CONDITIONS`</mark> | [<mark style="color:green;">`CustomRowNumericFilterCondition`</mark>](/reference/classes/filtering#customrownumericfiltercondition-less-than-t-greater-than)   |
| <mark style="color:blue;">`CUSTOM_ROW_DATE_FILTER_CONDITIONS`</mark>    | [<mark style="color:green;">`CustomRowDateFilterCondition`</mark>](/reference/classes/filtering#customrowdatefiltercondition-less-than-t-greater-than)         |

Let us define a condition for `Text` In-line Filters.

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

```typescript
@NgModule({
  providers: [
    {
      provide: CUSTOM_ROW_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomRowTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition<Employee>()
          .addExistingCondition(ConditionType.ENDS_WITH)
    }
  ]
})
export class FilteringModule { }

```

{% endcode %}

With the previous code snippet, we have changed the default condition to be **Ends With** for `Text` In-line Filters.

{% hint style="warning" %}
If you add more than one condition to the custom instance, the DikeGrid will take only the first one.
{% endhint %}

### Customization at the grid level

When you provide an Injection Token, you are changing conditions for all DikeGrid instances that live under the place you give the Injection Token.

Sometimes, you want to be specific for a particular DikeGrid instance. Then, to change conditions at the grid scope, you must provide the corresponding custom instance through an input property named <mark style="color:orange;">`gridCustomFilterConditions`</mark>.

{% hint style="info" %}
The <mark style="color:orange;">`gridCustomFilterConditions`</mark> property is of type [<mark style="color:green;">`DikeGridCustomFilterConditions`</mark>](/reference/type-aliases/filtering#dikegridcustomfilterconditions-less-than-t-greater-than). You **can not change** this property at **runtime**.
{% endhint %}

Let us provide custom conditions for `Date` and `Numeric` types.

{% tabs %}
{% tab title="in-line-filters.component.html" %}

```markup
<dike-grid id="grid-filter-types" height="600px" #grid="dkgGrid"
    [gridCustomFilterConditions]="gridCustomConditions">
</dike-grid>
```

{% endtab %}

{% tab title="in-line-filters.component.ts" %}

```typescript
ngOnInit(): void {
    //...
    
    // Defining filters at DikeGrid instance level:
    this.gridCustomConditions = {
      customRowDateFilterConditions: new CustomRowDateFilterCondition<Employee>()
        .addExistingCondition(ConditionType.GREATER_THAN),

      customRowNumericFilterConditions: new CustomRowNumericFilterCondition<Employee>()
        .addCondition({
          text: 'Custom Row Numeric',
          value: 'grid-customRowNumericMultipleOf',
          eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, number>, values?: DikeNumericFilter): boolean =>
            dataColumnDef.getValue(entry) % values.value1 === 0
        })
    };
  }
```

{% endtab %}
{% endtabs %}

We have changed the `Date` condition to be **Greater Than** and the `Numeric` condition to be **grid-customRowNumericMultipleOf**.

For `Date` types, we took an existing condition, and for `Numeric` types, we defined a new custom condition.

## Summary

In-line Filters are the **quickest** and **easiest** way to enable filtering. Every filter type has a default condition attached, but you can change it by providing a custom condition definition.

### Complete code for this section.

{% tabs %}
{% tab title="in-line-filters.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)="onGetFilterables()">Filterables
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onGetFilteredRows()">Filtered rows
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onClearFilter()">Clear filter
    </button>
</div>

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

    [allowRowFiltering]="gridProperties.allowRowFilter"
    [allowSorting]="gridProperties.allowSorting"
    [allowPagination]="gridProperties.allowPagination"

    (registerFilterableChange)="onRegisterChange($event)"
    (deregisterFilterableChange)="onDeregisterChange($event)"
    (filterChange)="onFilterChange($event)"
    [gridCustomFilterConditions]="gridCustomConditions"

    [datasource]="dkgDataSource">

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

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable>
        </dike-grid-column>

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

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

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

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

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

</dike-grid>
```

{% endtab %}

{% tab title="in-line-filters.component.ts" %}

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

import { DikeGridComponent, DikeGridDataSourceInput, DikeFilterable, DikeGridCustomFilterConditions,
    CustomRowDateFilterCondition, DikeDataColumnDef, CustomRowNumericFilterCondition,
    DikeNumericFilter, ConditionType
} 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: 'in-line-filters',
  templateUrl: './in-line-filters.component.html',
  styleUrls: ['./in-line-filters.component.scss'],

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;
  gridCustomConditions: DikeGridCustomFilterConditions<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();

    // Defining filters at DikeGrid instance level:
    this.gridCustomConditions = {
      customRowDateFilterConditions: new CustomRowDateFilterCondition<Employee>()
        .addExistingCondition(ConditionType.GREATER_THAN),

      customRowNumericFilterConditions: new CustomRowNumericFilterCondition<Employee>()
        .addCondition({
          text: 'Custom Row Numeric',
          value: 'grid-customRowNumericMultipleOf',
          eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, number>, values?: DikeNumericFilter): boolean =>
            dataColumnDef.getValue(entry) % values.value1 === 0
        })
    };
  }

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

  onGetFilterables(): void {
    console.log('Filterables: ', this.dikeGrid.filter.filterables);
  }

  onGetFilteredRows(): void {
    console.log('Filtered rows: ', this.dikeGrid.filter.getFilteredRows());
  }

  onClearFilter(): void {
    this.dikeGrid.filter.clearFilter();
  }

  onRegisterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Register change: ', filterable);
  }

  onDeregisterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Deregister change: ', filterable);
  }

  onFilterChange(filterable: DikeFilterable<Employee>): void {
    console.log('Filter change: ', filterable);
  }

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

```

{% endtab %}

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

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

import { DikeDataGridModule, NUMERIC_FILTER_DEBOUNCE_TIME,
    CUSTOM_ROW_TEXT_FILTER_CONDITIONS, CustomRowTextCaseFilterCondition, ConditionType,
    CUSTOM_TEXT_FILTER_CONDITIONS, CustomTextCaseFilterCondition,
    DikeDataColumnDef, DikeTextFilter } from '@dikesoft/angular-data-grid';

import { SharedModule } from 'app/shared/shared.module';
import { Employee } from 'app/mock-api/common/employees/data.model';
import { filteringRoutes } from 'app/modules/admin/filtering/filtering.routing';

import { ColumnFiltersComponent } from './column-filters/column-filters.component';
import { FilterTypesComponent } from './filter-types/filter-types.component';
import { InLineFiltersComponent } from './in-line-filters/in-line-filters.component';

@NgModule({
  declarations: [
    ColumnFiltersComponent,
    FilterTypesComponent,
    InLineFiltersComponent
  ],
  imports: [
    CommonModule,
    RouterModule.forChild(filteringRoutes),

    SharedModule,
    DikeDataGridModule
  ],
  providers: [
    { provide: NUMERIC_FILTER_DEBOUNCE_TIME, useValue: 300 },
    {
      provide: CUSTOM_ROW_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomRowTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition<Employee>()
          .addExistingCondition(ConditionType.ENDS_WITH)
    },
    {
      provide: CUSTOM_TEXT_FILTER_CONDITIONS,
      useFactory: (): CustomTextCaseFilterCondition<Employee> =>
        new CustomRowTextCaseFilterCondition()
          .addCondition({
            text: 'Custom Lower Case',
            value: 'global-customLowerCaseText',
            eval: (entry: Employee, dataColumnDef: DikeDataColumnDef<Employee, string>, values?: DikeTextFilter): boolean =>
              dataColumnDef.getValue(entry).toLowerCase().includes(values.value)
          })
    }
  ]
})
export class FilteringModule { }

```

{% endtab %}
{% endtabs %}


# Row Edition

This section describes how to enable the edition operations and perform the edition per row.

## Live example

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

## Allowing edition

To allow the user to edit the DikeGrid rows, you must provide an input property called <mark style="color:orange;">`allowEdition`</mark>.

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

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

{% endcode %}

{% hint style="warning" %}
You can not change the <mark style="color:orange;">`allowEdition`</mark> property at **runtime**.
{% endhint %}

### Edition mode

The <mark style="color:orange;">`allowEdition`</mark> property internally prepares the DikeGrid to support row edition, but you can only set this property at the creation phase.

The edition mode toggles the DikeGrid UI to show the necessary elements to execute edition operations. Therefore, you must provide an input property named <mark style="color:orange;">`editionMode`</mark>.

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

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

{% endcode %}

Open the [Floating Configuration Panel](/#floating-configuration-panel) for the live example and click on the **Edition mode** checkbox.

![Floating Configuration Panel - Edition](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F02z0J0RWkndtL2X7T3di%2Fgrid-structure-edition-panel-conf.png?alt=media\&token=d32fc072-66ce-4fec-b341-4f4f2827a466)

Once you have enabled the edition mode, you will see an icon (three vertical dots) at the left of every row. When you click on this icon, it will show a contextual menu with the edition operations.

![Enabling the Edition mode](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FlWlqng0CG0u43HrHFEqr%2Frow-edition-enabling-edition-mode.png?alt=media\&token=cd521c02-9c9d-4c9b-87f0-6ce1a2370341)

## Edition actions

Once you have allowed the edition operations and toggled the DikeGrid to the edition mode, you can take a row to the edition state, make some changes to its fields and then cancel the edition or save the changes. Of course, you can also delete the row.

Before exploring the **edition actions**, consider the following column configuration:

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

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

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

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            editable>
        </dike-grid-column>

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

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

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

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

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

</dike-grid>
```

{% endcode %}

We have defined all columns as **editable** except for **EmployeeId**, **Gender**, and **Email** columns.

{% hint style="info" %}
You can not make a column group **editable**.
{% endhint %}

### Row Status

When the DikeGrid wraps the provided entries, it assigns to the row a creation **timestamp**, and a status equals to <mark style="color:red;">`Read`</mark> value.

{% hint style="info" %}
The DikeGrid changes the row status and the timestamp every time an edition operation occurs.
{% endhint %}

The row status values are:

| Edition operation | Row status value                           |
| ----------------- | ------------------------------------------ |
| No operation      | <mark style="color:red;">`Read`</mark>     |
| Edition           | <mark style="color:red;">`Editing`</mark>  |
| Update            | <mark style="color:red;">`Modified`</mark> |
| Remove            | <mark style="color:red;">`Deleted`</mark>  |

### Editing a row

You can **double-click** on a row to change it to the edition state or click on the **Edit** option from the row context menu.

After changing a row to the edition state, the DikeGrid instance shows the default column templates for editing. It also displays an icon (a pencil) indicating editing that row.

![Changing a row to edition state](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FzU1nglVYnjUUxLPF0G90%2Frow-edition-row-edition-state.png?alt=media\&token=a779bf93-cf36-494b-8a42-31504d97db1b)

{% hint style="success" %}
See the Edition Templates section to customize the column templates for editing.
{% endhint %}

### Saving changes

After you have made the changes you needed, you can save those changes by pressing the **ENTER** key or clicking on the **Save** option from the row context menu.

{% hint style="info" %}
Be aware that the DikeGrid will save the edited row if it is **not pristine** and is **valid**. Therefore, the DikeGrid will save the row if you have changed it.
{% endhint %}

Let us add a simple validator to the **Name** column to see validation in action.

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

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

    <dike-grid-column
        fieldName="firstName"
        headerText="Name"
        dataType="Text"
        width="150"
        sortable
        editable
        [editionSettings]="{ required: true }">
    </dike-grid-column>
</dike-grid-column>
```

{% endcode %}

We have just made the **Name** column ***required***. If you do not provide a value for the **Name** field, the DikeGrid will show a **red bar** at the left of the **Name** field.

![Column Name - Required validator](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FxhBa7EsfCfHHsrfnhyKK%2Frow-edition-required-validator.png?alt=media\&token=7cb65e51-1162-41c3-a41d-9ad1747d0420)

When you click on the **red bar**, the DikeGrid will show you the **error message** coming from the validator.

{% hint style="success" %}
We have added a basic validator but very common. You can add more validators to a column, even your custom validators. See the [Edition validation](/editing/edition-validation) section for more details.
{% endhint %}

{% hint style="info" %}
After updating the row, the DikeGrid changes the row status to the <mark style="color:red;">`Modified`</mark> value.
{% endhint %}

### Canceling edition

You can cancel the row edition at any time by pressing the **ESC** key or clicking on the **Cancel** option from the row context menu.

{% hint style="info" %}
If you have made changes, you will lose those changes by canceling the row edition.
{% endhint %}

### Deleting a row

You can remove a row by clicking on the **Delete** option from the row context menu.

{% hint style="warning" %}
You can not delete a row if the row is in edition state.
{% endhint %}

{% hint style="info" %}
After deleting a row, the DikeGrid changes the row status to the <mark style="color:red;">`Deleted`</mark> value.
{% endhint %}

## Edition triggers

We named edition triggers those actions that help you interact with the DikeGrid rows in edition state.

There is one edition trigger per edition action except for deletion. Edition triggers work over a single row. We have seen every edition trigger in the previous sections. See the following table:

| Edition action | Edition trigger         |
| -------------- | ----------------------- |
| Edit           | **Double-click**        |
| Save           | Press the **ENTER** key |
| Cancel         | Press the **ESC** key   |

You can disable these edition triggers by providing the related **Injection Tokens** or at the **grid** level.

### Disabling triggers at the grid level

You must provide a <mark style="color:red;">`false`</mark> value to the corresponding flag through the input property named <mark style="color:orange;">`gridEditionSettings`</mark>.

{% hint style="info" %}
The <mark style="color:orange;">`gridEditionSettings`</mark> property is of type <mark style="color:green;">`DikeGridEditionSettings`</mark>. You must provide the corresponding flag related to edition triggers.
{% endhint %}

Let us disable the **ENTER** key corresponding to the **Save** action.

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

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

{% endtab %}

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

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

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class RowEditionComponent implements OnInit, OnDestroy {
  // ...
  
  editionSettings: DikeGridEditionSettings<Employee>;

  ngOnInit(): void {
    // Disable the ENTER edition trigger:
    this.editionSettings = { rowEditionEnterkey: false };
  }
}
```

{% endtab %}
{% endtabs %}

Please, open the live example and test if you can **save** a row by pressing the **ENTER** key.

{% hint style="info" %}
You can only set the <mark style="color:orange;">`gridEditionSettings`</mark> property at the **creation** phase.
{% endhint %}

### Disabling triggers by providing an Injection Token

Every edition trigger can be disabled by providing the related Injection Token.

| Edition trigger  | Injection Token                                          |
| ---------------- | -------------------------------------------------------- |
| **Double-click** | <mark style="color:blue;">`ROW_EDITION_DBLCLICK`</mark>  |
| **ENTER** Key    | <mark style="color:blue;">`ROW_EDITION_ENTER_KEY`</mark> |
| **ESC** Key      | <mark style="color:blue;">`ROW_EDITION_ESC_KEY`</mark>   |

Same as before, you must provide a <mark style="color:red;">`false`</mark> value to the corresponding Injection Token.

Let us disable the **ESC** key related to the **Cancel** action.

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

```typescript
@NgModule({
  providers: [
    { provide: ROW_EDITION_ESC_KEY, useValue: false }
  ]
})
export class EditingModule { }
```

{% endcode %}

Please, open the live example and test if you can **cancel** the row edition by pressing the **ESC** key.

## Editing using the API

You can perform the edition actions using the corresponding API.

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

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

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

{% endtab %}

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

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

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

  //...
}
```

{% endtab %}
{% endtabs %}

### DikeGrid Column API

Using the **column API**, you can make a column **editable** and establish its **edition settings** at **runtime**.

| Method                | Description                                            |
| --------------------- | ------------------------------------------------------ |
| `setColumnEditable()` | Use this method to set or unset a column **editable**. |

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

### DikeGrid Edition API

You can use the **edition API** for managing the edition actions.

The **methods** in the edition API are:

| Method               | Description                                                                                                                                                                                    |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `editRow()`          | This method will change the row status to **edition state**.                                                                                                                                   |
| `updateRow()`        | It **saves** the changes in the given row. The given row must be **valid** a **not pristine**. After updating, the DikeGrid sets the row status to <mark style="color:red;">`Modified`</mark>. |
| `cancelRowEdition()` | It changes the row status to <mark style="color:red;">`Read`</mark> status. This method discards all the changes in the given row.                                                             |
| `removeRow()`        | This method will change the row status to <mark style="color:red;">`Deleted`</mark> status.                                                                                                    |
| `restoreRow()`       | This method will revert any change that the user has made.                                                                                                                                     |

The **properties** in the edition API are:

| Property        | Description                                                |
| --------------- | ---------------------------------------------------------- |
| `rowsInEdition` | It returns the current rows in edition state.              |
| `modifiedRows`  | It returns all the rows that have changes in their fields. |
| `removedRows`   | It returns all the deleted rows.                           |

{% hint style="success" %}
For further details, see the [<mark style="color:green;">`DikeGridEdition`</mark>](/reference/dkgrid-api/dkgridedition) definition.
{% endhint %}

{% hint style="info" %}
You do not have to provide the <mark style="color:orange;">`editionMode`</mark> property for **edition API** use. It is enough to allow edition by providing the property named <mark style="color:orange;">`allowEdition`</mark>.
{% endhint %}

To see the API in action, let us define the following API:

{% tabs %}
{% tab title="row-edition.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)="onEditRow()"
            [disabled]="!isFiltered">editRow - filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onEmailEditable()">Email - editable
        </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)="onUpdateRow()">updateRow
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onRestoreRowModified()">restoreRow - modified
        </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)="onCancelRowEdition()">cancelRowEdition
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onRemoveRow()">removeRow - filtered
        </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)="onRestoreRowDeleted()">restoreRow - deleted
        </button>
    </div>
</div>
```

{% endtab %}

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

```typescript
onEditRow(): void {
  // Take all rows that are not being edited from filtered rows:
  const filteredRows = this.dikeGrid.filter.getFilteredRows().filter(row => !row.isEditing);

  // You can only have one row in edition state:
  if (filteredRows.length > 0 && this.dikeGrid.edition.rowsInEdition.length === 0) {
    // Take the first row and change it to the edition mode:
    this.dikeGrid.edition.editRow(filteredRows[0]);
  }
}

onEmailEditable(): void {
  // Get the Email column:
  const emailColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'email');

  // The column exist and is a data column:
  if (!!emailColumn && isDikeDataColumnDef(emailColumn)) {
    // Toggle the editable value:
    this.dikeGrid.columnDef.setColumnEditable(emailColumn, !emailColumn.editable, { editionSettings: { required: true } });
  }
}

onUpdateRow(): void {
  // Take the row in edition and update it:
  if (this.dikeGrid.edition.rowsInEdition.length === 1) {
    // The DikeGrid intance will not update the given row if the row is pristine and not valid:
    this.dikeGrid.edition.updateRow(this.dikeGrid.edition.rowsInEdition[0]);
  }
}

onRestoreRowModified(): void {
  // Take the row from the modified rows set:
  if (this.dikeGrid.edition.modifiedRows.length === 1) {
    // Restore the row to its immediate previous state:
    this.dikeGrid.edition.restoreRow(this.dikeGrid.edition.modifiedRows[0]);
  }
}

onCancelRowEdition(): void {
  // Take the row that it is being edited:
  if (this.dikeGrid.edition.rowsInEdition.length === 1) {
    this.dikeGrid.edition.cancelRowEdition(this.dikeGrid.edition.rowsInEdition[0]);
  }
}

onRemoveRow(): void {
  // Take all rows that are not being edited from filtered rows:
  const filteredRows = this.dikeGrid.filter.getFilteredRows().filter(row => !row.isEditing);

  // You can only have one row in edition state:
  if (filteredRows.length > 0) {
    // Remove the first row from the final set:
    this.dikeGrid.edition.removeRow(filteredRows[0]);
  }
}

onRestoreRowDeleted(): void {
  // Take the row from the deleted rows set:
  if (this.dikeGrid.edition.removedRows.length > 0) {
    // Restore the row to its immediate previous state:
    this.dikeGrid.edition.restoreRow(this.dikeGrid.edition.removedRows[0]);
  }
}

onRowsInEdition(): void {
  console.log('Rows in edition: ', this.dikeGrid.edition.rowsInEdition);
}

onModifiedRows(): void {
  console.log('Modified rows: ', this.dikeGrid.edition.modifiedRows);
}

onRemovedRows(): void {
  console.log('Removed rows: ', this.dikeGrid.edition.removedRows);
}
```

{% endtab %}
{% endtabs %}

The previous definition generates the following output:

![Row Edition - Using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FVuiwWeMxaaDIm2DXqYhD%2Frow-edition-using-api.png?alt=media\&token=b26fe40d-6d40-44b0-a8c7-7f38712087f5)

You can only have one row in edition state, and we have enabled the In-line filters.

We describe every button in the following list.

1. **Email - editable**. Click on this button to make the **Email** column editable or not editable. It toggles the current editable value, and it is marked as **required**.
2. **editRow - filtered**. It changes one row from filtered set to edition state. It will be disabled if there are no filtered rows.
3. **updateRow**. It saves the changes of the row in edition mode.
4. **restoreRow - modified**. It reverts changes from a modified row. You can revert all the changes you have made.
5. **cancelRowEdition**. It cancels the edition of the row in edition state.
6. **removeRow** **- filtered**. It takes a row from the filtered set and removes it.
7. **restoreRow - deleted**. It restores a row from the deleted rows set.
8. **Rows in edition**. It prints the row in edition mode.
9. **Modified rows**. It prints all the rows that have been modified.
10. **Removed rows**. It prints all the rows that have been deleted.

## Edition events

When performing the edition actions, the DikeGrid instance **emits** the corresponding events.

Events regarding edition actions are:

| Event                    | Description                                             |
| ------------------------ | ------------------------------------------------------- |
| `editionRowChange`       | It emits the row that has been changed to edition mode. |
| `updateRowChange`        | It emits the modified rows.                             |
| `cancelRowEditionChange` | It emits the rows that have been canceled for edition.  |
| `removeRowChange`        | It emits the rows that have been deleted.               |
| `restoreRowChange`       | It emits the rows whose changes were reverted.          |

Let us listen to these events:

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

```markup
<dike-grid id="grid-row-edition" height="600px" #grid="dkgGrid"
    (editionRowChange)="onEditionRowChange($event)"
    (updateRowChange)="onUpdateRowChange($event)"
    (cancelRowEditionChange)="onCancelRowEditionChange($event)"
    (removeRowChange)="onRemoveRowChange($event)"
    (restoreRowChange)="onRestoreRowChange($event)">
</dike-grid>
```

{% endtab %}

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

```typescript
onEditionRowChange(value: DikeGridDataRowEntry<Employee>): void {
  console.log('editionRowChange: ', value);
}

onUpdateRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('updateRowChange: ', value);
}

onCancelRowEditionChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('cancelRowEditionChange: ', value);
}

onRemoveRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('removeRowChangeChange: ', value);
}

onRestoreRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('restoreRowChange: ', value);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
Please, open your **dev console** to see the output of these events.
{% endhint %}

{% hint style="success" %}
You can also listen to these events from the [<mark style="color:green;">`DikeGridEdition`</mark>](/reference/dkgrid-api/dkgridedition#events) instance.
{% endhint %}

## Summary

You allow the edition by providing a property named <mark style="color:orange;">`allowEdition`</mark>. Then, to let the user **toggle** to the **edition mode**, provide a property called <mark style="color:orange;">`editioMode`</mark>. Finally, you define a column as **editable** to modify the column value.&#x20;

In this section, we have explored how to edit one row at a time using the UI or the API.

### Complete code for this section

{% tabs %}
{% tab title="row-edition.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)="onEditRow()"
            [disabled]="!isFiltered">editRow - filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onEmailEditable()">Email - editable
        </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)="onUpdateRow()">updateRow
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onRestoreRowModified()">restoreRow - modified
        </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)="onCancelRowEdition()">cancelRowEdition
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onRemoveRow()">removeRow - filtered
        </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)="onRestoreRowDeleted()">restoreRow - deleted
        </button>
    </div>
</div>

<mat-divider class="m-6"></mat-divider>

<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)="onRowsInEdition()">Rows in edition
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onModifiedRows()">Modified rows
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onRemovedRows()">Removed rows
    </button>
</div>

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

    allowEdition
    [editionMode]="gridProperties.editionMode"
    [allowSorting]="gridProperties.allowSorting"
    [allowRowFiltering]="gridProperties.allowRowFilter"

    [gridEditionSettings]="editionSettings"

    (editionRowChange)="onEditionRowChange($event)"
    (updateRowChange)="onUpdateRowChange($event)"
    (cancelRowEditionChange)="onCancelRowEditionChange($event)"
    (removeRowChange)="onRemoveRowChange($event)"
    (restoreRowChange)="onRestoreRowChange($event)"
    (filterChange)="onFilterChange($event)"

    [datasource]="dkgDataSource">

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

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

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            editable
            [editionSettings]="{ required: true }">
        </dike-grid-column>

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

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

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

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

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

</dike-grid>
```

{% endtab %}

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

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

import { DikeFilterable, DikeGridComponent, DikeGridDataRowEntry,
    DikeGridDataSourceInput, DikeGridEditionSettings, 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: 'row-edition',
  templateUrl: './row-edition.component.html',
  styleUrls: ['./row-edition.component.scss'],

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  editionSettings: DikeGridEditionSettings<Employee>;
  isFiltered: boolean;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

    this.isFiltered = false;
  }

  ngOnInit(): void {
    // Get 1000 entries from the REST API:
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
    // Disable the ENTER edition trigger:
    this.editionSettings = { rowEditionEnterkey: false };
  }

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

  onEditRow(): void {
    // Take all rows that are not being edited from filtered rows:
    const filteredRows = this.dikeGrid.filter.getFilteredRows().filter(row => !row.isEditing);

    // You can only have one row in edition state:
    if (filteredRows.length > 0 && this.dikeGrid.edition.rowsInEdition.length === 0) {
      // Take the first row and change it to the edition mode:
      this.dikeGrid.edition.editRow(filteredRows[0]);
    }
  }

  onEmailEditable(): void {
    // Get the Email column:
    const emailColumn = this.dikeGrid.columnDef.findColumn(column => column.fieldName === 'email');

    // The column exist and is a data column:
    if (!!emailColumn && isDikeDataColumnDef(emailColumn)) {
      // Toggle the editable value:
      this.dikeGrid.columnDef.setColumnEditable(emailColumn, !emailColumn.editable, { editionSettings: { required: true } });
    }
  }

  onUpdateRow(): void {
    // Take the row in edition and update it:
    if (this.dikeGrid.edition.rowsInEdition.length === 1) {
      // The DikeGrid intance will not update the given row if the row is pristine and not valid:
      this.dikeGrid.edition.updateRow(this.dikeGrid.edition.rowsInEdition[0]);
    }
  }

  onRestoreRowModified(): void {
    // Take the row from the modified rows set:
    if (this.dikeGrid.edition.modifiedRows.length === 1) {
      // Restore the row to its immediate previous state:
      this.dikeGrid.edition.restoreRow(this.dikeGrid.edition.modifiedRows[0]);
    }
  }

  onCancelRowEdition(): void {
    // Take the row that it is being edited:
    if (this.dikeGrid.edition.rowsInEdition.length === 1) {
      this.dikeGrid.edition.cancelRowEdition(this.dikeGrid.edition.rowsInEdition[0]);
    }
  }

  onRemoveRow(): void {
    // Take all rows that are not being edited from filtered rows:
    const filteredRows = this.dikeGrid.filter.getFilteredRows().filter(row => !row.isEditing);

    // You can only have one row in edition state:
    if (filteredRows.length > 0) {
      // Remove the first row from the final set:
      this.dikeGrid.edition.removeRow(filteredRows[0]);
    }
  }

  onRestoreRowDeleted(): void {
    // Take the row from the deleted rows set:
    if (this.dikeGrid.edition.removedRows.length > 0) {
      // Restore the row to its immediate previous state:
      this.dikeGrid.edition.restoreRow(this.dikeGrid.edition.removedRows[0]);
    }
  }

  onRowsInEdition(): void {
    console.log('Rows in edition: ', this.dikeGrid.edition.rowsInEdition);
  }

  onModifiedRows(): void {
    console.log('Modified rows: ', this.dikeGrid.edition.modifiedRows);
  }

  onRemovedRows(): void {
    console.log('Removed rows: ', this.dikeGrid.edition.removedRows);
  }

  onEditionRowChange(value: DikeGridDataRowEntry<Employee>): void {
    console.log('editionRowChange: ', value);
  }

  onUpdateRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('updateRowChange: ', value);
  }

  onCancelRowEditionChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('cancelRowEditionChange: ', value);
  }

  onRemoveRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('removeRowChangeChange: ', value);
  }

  onRestoreRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('restoreRowChange: ', value);
  }

  onFilterChange(filterable: DikeFilterable<Employee>): void {
    // We ignore the argument filterable because we are only interested in the action.
    this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
  }

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

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


# Edition templates

This section describes how to provide custom templates for editing the column values.

## 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>](/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>](/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 %}


# Edition validation

This section describes how you can validate user input and show validation messages.

## 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>](/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>](/reference/interfaces/editing#crossfieldvalidationsettings-less-than-t-greater-than) and [<mark style="color:green;">`DikeGridEditionSettings`</mark>](/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>](/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 %}


# Multiple rows edition

This section describes how to update, delete or restore more than one row at a time.

## Live example

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

## Edition actions

Before exploring the edition actions, do not forget to **allow edition** and enable the **edition mode**.

{% hint style="info" %}
Remember that the edition mode will display all the UI elements for editing rows.
{% endhint %}

We start defining some **editable** columns.

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

```markup
<dike-grid id="grid-multiple-rows-edition" 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
            [editionSettings]="{ required: true }">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            editable
            [editionSettings]="{ required: true }">
        </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]="{ required: true }">
    </dike-grid-column>

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

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        editable
        [editionSettings]="{ required: true }">
    </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>
```

{% endcode %}

We have configured the following features:

1. All the columns are **editable** except for the **Employee Id** column.
2. All editable columns are **required**, and we have set **templates** for the **Gender** and **Email** columns.
3. You can toggle to the **edition mode** from the [Floating Configuration Panel](/#floating-configuration-panel).

When the DikeGrid is in the edition mode, it shows an **icon** (**three vertical dots**) at the left of every row. When you click on this icon, it will show a contextual menu with the edition operations per row.

At the same time, the DikeGrid shows an **icon** (**three horizontal dots**) in the header at the left of the first visible column header. When you click on this icon, it will show a contextual menu with the edition operations for multiple rows.

![Multiple rows edition - Contextual menu](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FMQo6gyCqoHMCNkuR5EuS%2Fmultiple-rows-edition-contextual-menu.png?alt=media\&token=1ad146d6-adc0-4c0f-ad9a-54b173f009df)

### Editing rows

Let us start editing some rows by **double-clicking** on them. This action will display the edition templates for each **editable** column.

The DikeGrid allows you to have up to **10 rows** in edition state by default, but you can change this value by providing the corresponding value.

You can change the maximum rows in the edition state value by providing an **Injection Token** or at the **grid** level.

| Variable                                                                                   | Description                                                                                             |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| <mark style="color:blue;">`MAX_ROWS_IN_EDITION`</mark>                                     | This value affects all your DikeGrid instances that live under the place you give your Injection Token. |
| <mark style="color:green;">`DikeGridEditionSettings { maxRowsInEdition?: number };`</mark> | Providing at this level only affects the DikeGrid instance that receives the value.                     |

Let us provide the <mark style="color:blue;">`MAX_ROWS_IN_EDITION`</mark> token:

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

```typescript
@NgModule({
  providers: [
    { provide: MAX_ROWS_IN_EDITION, useValue: 5 }
  ]
})
export class EditingModule { }
```

{% endcode %}

When you reach the maximum number of rows in the edition state, the DikeGrid will show you a message indicating it.

![Maximum rows in the edition state message](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FTj2jeSgHvvT3ahQTgqQ6%2Fmultiple-rows-edition-reach-max-rows.png?alt=media\&token=6bcf9653-9363-48e1-8924-6300fb6ab26c)

### Saving rows

Once you have made some changes to some rows, you can save these rows by clicking on the **Save** option from the contextual menu in the header.

{% hint style="warning" %}
Be aware that the DikeGrid will enable the **Save** option if all rows in the edition state are **valid** and at least one is **not pristine**. Therefore, the DikeGrid will save those valid rows which are not pristine.
{% endhint %}

{% hint style="info" %}
After updating rows, the DikeGrid changes the status of every row to the <mark style="color:red;">`Modified`</mark> value.
{% endhint %}

{% hint style="success" %}
Please, open the live example to test the **Save** operation.
{% endhint %}

### Canceling edition

When you have some rows in the edition state, the first enabled option in the contextual menu is **Cancel**.

Clicking on the Cancel option in the context menu will cancel the edition of all the rows in the edition state.

{% hint style="warning" %}
If you have made some changes, you will lose those changes by canceling the edition.
{% endhint %}

### Deleting rows

To delete rows from the DikeGrid UI, you must enable the **selection** operation. Therefore, you can delete only the selected rows from the UI.

The **Remove** option appears in the contextual menu if you allow the user to select rows, and the DikeGrid will enable it only when the user has selected one row at least.

Let us provide the corresponding input property to enable the selection operation.

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

```markup
<dike-grid id="grid-multiple-rows-edition" height="600px" #grid="dkgGrid"
    [allowSelection]="gridProperties.allowSelection">
</dike-grid>
```

{% endcode %}

You can open the [Floating Configuration Panel](/#floating-configuration-panel) and click on the **Allow Selection** checkbox.

![Floating Configuration Panel - Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FR0ZZq1Pf1grFV8IN86tZ%2Fselection-panel-conf.png?alt=media\&token=1756c6f8-11e7-499d-a420-228c9c2e57cb)

Now the context menu shows the **Remove** option.

![Multiple rows edition - Remove option](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FHO3zxtEQ66DeFqsrhxQj%2Fmultiple-rows-edition-delete-option.png?alt=media\&token=0322e171-27bc-472a-a042-1b50848b50da)

{% hint style="info" %}
If the user has selected rows in the edition state, the DikeGrid will not delete them.
{% endhint %}

## Reverting changes

You can undo changes from the modified or deleted rows. Therefore, you have to change the view from the UI to display only **modified** or **deleted** rows.

### Edition filters

If you open the context menu, you can see an option called **Edition Filter**, and under this option, you will find the edition subsets options.

![Multiple rows edition - Edition filters](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F6ysQJormdIHERXI8Fehs%2Fmultiple-rows-edition-edition-filters.png?alt=media\&token=0b87ccd4-0aac-44f0-88b6-02d1d30caf0d)

{% hint style="info" %}
The DikeGrid will take the user to the **edition view** by clicking on these edition subsets options.
{% endhint %}

### Option - Editing

Clicking on this menu option will display only the rows in the edition state. Showing only the rows editing is helpful when you have grouped the rows, for instance, because the rows in the edition state could be in different groups.

![Edition Filter - Editing option](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FcJLwl2Fv9dsZcn2Hu8Tb%2Fmultiple-rows-edition-edition-filters-editing.png?alt=media\&token=8e89b4f3-8964-4f34-90d2-02f30cf20030)

As you can see, the icon in the header changes to a **pencil icon** (instead of the three horizontal dots), indicating that you are viewing only the rows in the edition state.

### Option - Modified

This menu option shows only those rows that have suffered changes. When you navigate to the **modified-rows** view, you will see an icon with a **badge** at the left of every row. The badge indicates how many times the user has changed the row.

![Edition Filter - Modified option](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4WgLo8mLmK7OvAYVB9lp%2Fmultiple-rows-edition-edition-filters-modified.png?alt=media\&token=7377fc63-a813-422c-9df0-7c7be1dbe17a)

You click on the icon with a badge to **revert** the changes in the row. This action will take the row to its **immediate previous state**.

{% hint style="success" %}
You can click on the icon with a badge until the row reaches its initial state. The initial state corresponds to the row state equals to <mark style="color:red;">`Read`</mark> value.
{% endhint %}

If you have enabled the **selection** operation, you can select more than one row and take them to their immediate previous state by clicking on the **Restore** option from the contextual menu.

![Restoring changes for multiple rows](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fh1wrusQ6zEJgO9s9OoZ7%2Fmultiple-rows-edition-restore-selected.png?alt=media\&token=1de518ee-6241-424b-95b0-4bb8c7e9b47e)

Once again, see how the DikeGrid changes the icon to indicate that you are viewing the **modified** rows.

### Option - Removed

This menu option shows only the subset of rows that corresponds to the deleted rows. When you are in the **deleted-rows** view, you will see an icon with a **badge** at the left of every row. Again, the badge indicates how many times the user has changed the row, but this time the last action corresponds to the deletion operation.

![Edition Filter - Removed option](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FgiMyOcy36u2u72znZuVT%2Fmultiple-rows-edition-edition-filters-removed.png?alt=media\&token=a34992ab-58ee-43f2-8d26-6615b27d03b8)

{% hint style="info" %}
If the badge displays a number greater than one means that the row has suffered changes before deleting it.
{% endhint %}

The DikeGrid changes the icon in the header to indicate that you are viewing the **deleted** rows.

If you have enabled the selection operation, you can select more than one row and **restore** them by clicking on the **Restore** option from the contextual menu.

![Restoring multiple removed rows](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FM4gKM225SFIZQzuOFTcj%2Fmultiple-rows-edition-restore-removed-selected.png?alt=media\&token=3017fc02-17e9-4d91-b9b6-9493732e57a6)

### Option - Back

You can display the **standard view** by clicking on the **Back** option.

{% hint style="success" %}
You can filter every edition subset of rows by typing values in a **filterable** column. The values you enter in the **Edition Filters** view are independent of the values in the standard view.
{% endhint %}

## Edition Toolbar

All the edition actions for multiple rows are available through the Edition Toolbar. When you display the Edition Toolbar, the DikeGrid hides the contextual menu in the header.

If you want to use the Edition Toolbar in the live example, open the [Floating Configuration Panel](/#floating-configuration-panel) and click on the **Edition Toolbar** checkbox.

![Floating Configuration Panel - Edition](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F02z0J0RWkndtL2X7T3di%2Fgrid-structure-edition-panel-conf.png?alt=media\&token=d32fc072-66ce-4fec-b341-4f4f2827a466)

When displaying the Edition Toolbar, the DikeGrid still shows the icon in the header but does not show the contextual menu.

![Multiple rows edition - Toolbar](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FTysuh3oKCMdBSYmjoUuH%2Fmultiple-rows-edition-toolbar.png?alt=media\&token=a1d5f7ee-4bd9-4b99-9afa-cdf35d3e26d2)

### Customizing the Edition Toolbar

You can change the appearance of the Edition Toolbar by modifying its **height**, **alignment**, or **position**. You can also change the **size** of its items.

{% hint style="success" %}
For further details, see the [Grid Structure - Edition Toolbar](/fundamentals/grid-structure/edition-toolbar) section.
{% endhint %}

## DikeGrid Edition API

You can perform the edition actions using the **edition API**.

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

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

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

{% endtab %}

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

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

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

{% endtab %}
{% endtabs %}

The following method operates over multiple rows edition.

| Method                  | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `updateRowsInEdition()` | This method takes all valid rows which are not pristine and saves their changes.                                                                                                                                                                                                                                                                                                                                                                                     |
| `removeRows()`          | It removes from the given rows only those rows that are not in the edition state and do not have the <mark style="color:red;">`Deleted`</mark> value in their row status.                                                                                                                                                                                                                                                                                            |
| `cancelRowsInEdition()` | It cancels the current edition state for every row, losing all the changes you have made.                                                                                                                                                                                                                                                                                                                                                                            |
| `restoreRows()`         | It takes the given rows to their immediate previous row state. It filters the given rows taking only those **modified** or **deleted** rows.                                                                                                                                                                                                                                                                                                                         |
| `setEditionFilter()`    | <p>It displays the corresponding subset of rows editing. Invoking this method is equivalent to clicking on the contextual menu's <strong>Editing</strong>, <strong>Modified</strong>, or <strong>Removed</strong> options.</p><p>Since this method changes the view of the DikeGrid, you should have provided the <mark style="color:orange;"><code>allowEdition</code></mark> and <mark style="color:orange;"><code>editionMode</code></mark> input properties.</p> |
| `cancelEditionFilter()` | Invoking this method, the DikeGrid will show the **standard view**. It is equivalent to clicking on the **Back** option of the contextual menu.                                                                                                                                                                                                                                                                                                                      |

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

{% hint style="info" %}
It is enough to set the input property called <mark style="color:orange;">`allowEdition`</mark> to use the edition API.
{% endhint %}

To see the API in action, consider the following additions to the UI:

{% tabs %}
{% tab title="multiple-rows-edition.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)="onSaveRows()">Save rows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onCancelEdition()">Cancel Edition
        </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)="onRemoveRows()">Remove rows
        </button>
    </div>

    <div class="flex-none w-60 flex flex-col m-2 items-center">
        <mat-radio-group class="flex-none w-56 flex flex-row justify-between" [formControl]="rowTypecontrol">
            <mat-radio-button value="modified">Modified</mat-radio-button>
            <mat-radio-button value="removed">Removed</mat-radio-button>
          </mat-radio-group>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onRestoreRows()">Restore rows
        </button>
    </div>
</div>

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

{% endtab %}

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

```typescript
onSaveRows(): void {
  this.dikeGrid.edition.updateRowsInEdition();
}

onCancelEdition(): void {
  this.dikeGrid.edition.cancelRowsInEdition();
}

onRemoveRows(): void {
  // Delete the selected rows:
  this.dikeGrid.edition.removeRows(this.dikeGrid.selection.getSelectedRows());
}

onRestoreRows(): void {
  if (this.rowTypecontrol.valid) {
    const rows = this.rowTypecontrol.value as string === 'modified' ? this.dikeGrid.edition.modifiedRows :
      this.dikeGrid.edition.removedRows;

    this.dikeGrid.edition.restoreRows(rows);
  }
}
```

{% endtab %}
{% endtabs %}

The previous definition generates the following output:

![Multiple rows edition using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F7BWRSSSpEg5jv8jOT9mK%2Fmultiple-rows-edition-using-api.png?alt=media\&token=071cbde3-f147-4dcf-a4eb-89c2338f082e)

We have added the following buttons:

1. **Save rows**. This action updates the rows in edition state. Remember that the DikeGrid will save only those valid rows that are not pristine.
2. **Cancel Edition**. Clicking on this button will cancel the edition state for every row editing.
3. **Remove rows**. Click on this button to delete the selected rows. The DikeGrid will not remove the rows in the edition state.
4. **Restore rows**. This action works over the **modified** or **deleted** rows. Therefore, you select which type of rows the DikeGrid will restore.

## Edition events

The edition events for multiple rows are the same when working over one row. Regarding the various rows edition, the DikeGrid emits an array of rows instead of one row.

We listen to these events:

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

```markup
<dike-grid id="grid-multiple-rows-edition" height="600px" #grid="dkgGrid"
    (editionRowChange)="onEditionRowChange($event)"
    (updateRowChange)="onUpdateRowChange($event)"
    (cancelRowEditionChange)="onCancelRowEditionChange($event)"
    (removeRowChange)="onRemoveRowChange($event)"
    (restoreRowChange)="onRestoreRowChange($event)">
    
</dike-grid>
```

{% endtab %}

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

```typescript
onEditionRowChange(value: DikeGridDataRowEntry<Employee>): void {
  console.log('editionRowChange: ', value);
}

onUpdateRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('updateRowChange: ', value);
}

onCancelRowEditionChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('cancelRowEditionChange: ', value);
}

onRemoveRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('removeRowChangeChange: ', value);
}

onRestoreRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
  console.log('restoreRowChange: ', value);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
You can also listen to these events from the [<mark style="color:green;">`DikeGridEdition`</mark>](/reference/dkgrid-api/dkgridedition#events) instance
{% endhint %}

{% hint style="success" %}
Please, open the **dev console** to see the events' output.
{% endhint %}

## Blocking the UI

When you click on any edition option from the contextual menu, the DikeGrid shows the **Processing Indicator** to block its UI until the updating ends.

{% hint style="info" %}
For further details on blocking the DikeGrid UI, see the [Grid Structure - Waiting Indicator](/fundamentals/grid-structure/waiting-indicator) section.
{% endhint %}

You can avoid the DikeGrid displaying the Processing Indicator by providing a <mark style="color:red;">`false`</mark> value through the related **Injection Token** or at the **grid** level.

You can provide any of the following Injection Tokens depending on the edition action:

| Edition action | Injection Token                                                       |
| -------------- | --------------------------------------------------------------------- |
| Save           | <mark style="color:blue;">`WAIT_FOR_MULTIPLE_ROWS_UPDATE`</mark>      |
| Cancel         | <mark style="color:blue;">`WAIT_FOR_MULTIPLE_ROWS_CANCELATION`</mark> |
| Remove         | <mark style="color:blue;">`WAIT_FOR_MULTIPLE_ROWS_DELETION`</mark>    |
| Restore        | <mark style="color:blue;">`WAIT_FOR_MULTIPLE_ROWS_RESTORATION`</mark> |

If you want to apply the value for a particular DikeGrid instance, provide the corresponding variable through the <mark style="color:green;">`DikeGridEditionsSettings`</mark> reference:

| Edition action | Variable                         |
| -------------- | -------------------------------- |
| Save           | `waitForMultipleRowsUpdate`      |
| Cancel         | `waitForMultipleRowsCancelation` |
| Remove         | `waitForMultipleRowsDeletion`    |
| Restore        | `waitForMultipleRowsRestoration` |

Let us provide the <mark style="color:blue;">`WAIT_FOR_MULTIPLE_ROWS_CANCELATION`</mark> Injection Token to avoid displaying the Processing Indicator when canceling the edition.

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

```typescript
@NgModule({
  providers: [
    { provide: WAIT_FOR_MULTIPLE_ROWS_CANCELATION, useValue: false }
  ]
})
export class EditingModule { }
```

{% endcode %}

## Summary

Apart from saving changes for one row, you can **update multiples rows** simultaneously. However, be aware that you must **enable** the **selection** operation for **deleting** and **restoring** multiple rows.

You must navigate to the edition view for **restoring** rows by clicking on the corresponding **edition filter**. Every row shows an icon with a **badge** conveying how many times the row has suffered changes.

You can perform the edition actions for multiple rows using the **edition API**. Moreover, the DikeGrid **emits** the related **event** for every edition action.

### Complete code for this section

{% tabs %}
{% tab title="multiple-rows-edition.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)="onSaveRows()">Save rows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onCancelEdition()">Cancel Edition
        </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)="onRemoveRows()">Remove rows
        </button>
    </div>

    <div class="flex-none w-60 flex flex-col m-2 items-center">
        <mat-radio-group class="flex-none w-56 flex flex-row justify-between" [formControl]="rowTypecontrol">
            <mat-radio-button value="modified">Modified</mat-radio-button>
            <mat-radio-button value="removed">Removed</mat-radio-button>
          </mat-radio-group>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onRestoreRows()">Restore rows
        </button>
    </div>
</div>

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

    allowEdition
    [editionMode]="gridProperties.editionMode"
    [allowSelection]="gridProperties.allowSelection"
    [editionToolbar]="gridProperties.displayEditionToolbar"
    [editionToolbarRowHeight]="gridProperties.editionToolbarHeight"
    [editionToolbarItemHeight]="gridProperties.edtitionToolbarItemHeight"
    [editionToolbarPosition]="gridProperties.editionToolbarPosition"
    [editionToolbarAlignment]="gridProperties.editionToolbarAlignment"

    (editionRowChange)="onEditionRowChange($event)"
    (updateRowChange)="onUpdateRowChange($event)"
    (cancelRowEditionChange)="onCancelRowEditionChange($event)"
    (removeRowChange)="onRemoveRowChange($event)"
    (restoreRowChange)="onRestoreRowChange($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
            [editionSettings]="{ required: true }">
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            editable
            [editionSettings]="{ required: true }">
        </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]="{ required: true }">
    </dike-grid-column>

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

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        editable
        [editionSettings]="{ required: true }">
    </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>

```

{% endtab %}

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

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

import { DikeGridComponent, DikeGridDataRowEntry, DikeGridDataSourceInput, EditionFieldSettings } 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: 'multiple-rows-edition',
  templateUrl: './multiple-rows-edition.component.html',
  styleUrls: ['./multiple-rows-edition.component.scss'],

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

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  emailEditionSettings: EditionFieldSettings;
  rowTypecontrol: FormControl;

  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();
    // Set validators for the Email column:
    this.emailEditionSettings = { required: true, validators: [ Validators.email ] };

    this.rowTypecontrol = new FormControl('modified', Validators.required);
  }

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

  onSaveRows(): void {
    this.dikeGrid.edition.updateRowsInEdition();
  }

  onCancelEdition(): void {
    this.dikeGrid.edition.cancelRowsInEdition();
  }

  onRemoveRows(): void {
    // Delete the selected rows:
    this.dikeGrid.edition.removeRows(this.dikeGrid.selection.getSelectedRows());
  }

  onRestoreRows(): void {
    if (this.rowTypecontrol.valid) {
      const rows = this.rowTypecontrol.value as string === 'modified' ? this.dikeGrid.edition.modifiedRows :
        this.dikeGrid.edition.removedRows;

      this.dikeGrid.edition.restoreRows(rows);
    }
  }

  onEditionRowChange(value: DikeGridDataRowEntry<Employee>): void {
    console.log('editionRowChange: ', value);
  }

  onUpdateRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('updateRowChange: ', value);
  }

  onCancelRowEditionChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('cancelRowEditionChange: ', value);
  }

  onRemoveRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('removeRowChangeChange: ', value);
  }

  onRestoreRowChange(value: DikeGridDataRowEntry<Employee> | DikeGridDataRowEntry<Employee>[]): void {
    console.log('restoreRowChange: ', value);
  }

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

```

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


# DkGrid API


# DkGridColumnDef

You can access this instance through the property named columnDef of the DkGridComponent, or by listening to the DkGridComponent event called gridColumnDefInstance.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.&#x20;

<mark style="color:green;">`<R>`</mark> represents the column data type: <mark style="color:green;">`string`</mark>, <mark style="color:green;">`number`</mark>, <mark style="color:green;">`Date`</mark>, or <mark style="color:green;">`boolean`</mark>.

## Methods

| Method                                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |                                                                                                                                                              |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `addColumns()`                         | <p>It inserts a new column or a set of new columns.<br><br><strong>Params</strong>: </p><ol><li>columns: <mark style="color:green;"><code>DkColumnDef</code></mark>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | <mark style="color:green;"><code>DkColumnDef\[]</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p> |
| `removeColumns()`                      | <p>It deletes the given columns permanently.<br></p><p><strong>Params</strong>: </p><ol><li>columns: <mark style="color:green;"><code>DkColumnDef</code></mark>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | <mark style="color:green;"><code>DkColumnDef\[]</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p> |
| `findColumn()`                         | <p>It searches a column in all panels. The found column must meet the compare function criteria.<br></p><p><strong>Params</strong>: </p><ol><li>compare: <mark style="color:green;"><code>CompareColumnFn</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>DkColumnDef</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                |                                                                                                                                                              |
| `findNode()`                           | <p>It searches a node in all panels. The found node must meet the compare function criteria.<br></p><p><strong>Params</strong>: </p><ol><li>compare: <mark style="color:green;"><code>CompareNodeFn</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>DkNodeColumnDef</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                  |                                                                                                                                                              |
| `setColumnVisible()`                   | <p>It makes <strong>visible</strong> or <strong>non-visible</strong> the given column.<br></p><p><strong>Params</strong>: </p><ol><li>column: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li><li>isVisible: <mark style="color:green;"><code>boolean</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                           |                                                                                                                                                              |
| `changeColumnWidth<R>()`               | <p>It modifies a data column in width. You can set the minimum and the maximum values.</p><p><br><strong>Params</strong>: </p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li><li>columnSize: { width: <mark style="color:green;"><code>number</code></mark>, minWidth?: <mark style="color:green;"><code>number</code></mark>, maxWidth?: <mark style="color:green;"><code>number</code></mark> }.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                 |                                                                                                                                                              |
| `setColumnGroupable()`                 | <p>It makes the given column <strong>groupable</strong> or <strong>non-groupable</strong>.<br></p><p><strong>Params</strong>: </p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li><li>isGroupable: <mark style="color:green;"><code>boolean</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                          |                                                                                                                                                              |
| `lockColumn()`                         | <p>It locks the column for moving. Remember that the column must be <strong>draggable</strong>.<br><br><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                                 |                                                                                                                                                              |
| `unlockColumn()`                       | <p>It unlocks the column for moving. Remember that the column must be <strong>draggable</strong>.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                            |                                                                                                                                                              |
| `setColumnResizable()`                 | <p>It makes the given column <strong>resizable</strong> or <strong>non-resizable</strong>.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li><li>isResizable: <mark style="color:green;"><code>boolean</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                      |                                                                                                                                                              |
| `swapColumns()`                        | <p>It interchanges the source and destination columns. Columns must belong to the same group.<br></p><p><strong>Params</strong>:</p><ol><li>sourceColumn: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li><li>destinationColumn: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                   |                                                                                                                                                              |
| `moveColumnBefore()`                   | <p>It moves the given column at the left of the target column. Columns must belong to the same group.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li><li>targetColumn: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                      |                                                                                                                                                              |
| `moveColumnAfter()`                    | <p>It moves the given column to the right side of the target column. Columns must belong to the same group.</p><p></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li><li>targetColumn: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                             |                                                                                                                                                              |
| `moveColumnIntoPanel()`                | <p>It moves the given column to the given panel.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkColumnDef</code></mark>.</li><li>panel: <mark style="color:green;"><code>DkGridPanel</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                  |                                                                                                                                                              |
| `movePanelColumns()`                   | <p>It moves all columns from the fromPanel panel to the destinationPanel panel. You can move only visible columns by providing the onlyVisibleColumns parameter.<br></p><p><strong>Params</strong>:</p><ol><li>fromPanel: <mark style="color:green;"><code>DkGridPanel</code></mark>.</li><li>destinationPanel: <mark style="color:green;"><code>DkGridPanel</code></mark>.</li><li>onlyVisibleColumns: <mark style="color:green;"><code>boolean</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                |                                                                                                                                                              |
| `setColumnFilterability<R>()`          | <p>It changes the given column as <strong>filterable</strong> or not. You can specify an initial filter, and you can also define your custom filter conditions.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DikeDataColumnDef\<T, R></code></mark>.</li><li>isFilterable: <mark style="color:green;"><code>boolean</code></mark>.</li><li>filter?: <mark style="color:green;"><code>DikeFilter\[]</code></mark>.</li><li>customFilterConditions?: <mark style="color:green;"><code>CustomFilterConditionInstance\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p> |                                                                                                                                                              |
| `changeColumnFilter<R>()`              | <p>This method establishes a new filter for the given column. The column must be filterable.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li><li>filter: <mark style="color:green;"><code>DkFilter\[]</code></mark>.</li></ol><p><strong>Return</strong>: void.</p>                                                                                                                                                                                                                                                                                                                         |                                                                                                                                                              |
| `setColumnEditable<R>()`               | <p>It changes the given column as <strong>editable</strong> or not. You can specify the edition setting and the edition template for the given column.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li><li>isEditable: <mark style="color:green;"><code>boolean</code></mark>.</li><li>settings: { editionSettings?: <mark style="color:green;"><code>EditionFieldSettings</code></mark>, editionTemplate?: <mark style="color:green;"><code>TemplateRef\<any></code></mark> }.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>               |                                                                                                                                                              |
| `setDefaultEditionTemplateColumn<R>()` | <p>It sets the default edition template again.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                                                                    |                                                                                                                                                              |
| `setDisplayTemplateColumn<R>()`        | <p>It establishes the given template for the given column.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li><li>displayTemplate: <mark style="color:green;"><code>TemplateRef\<any></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                             |                                                                                                                                                              |
| `setDefaultDisplayTemplateColumn<R>()` | <p>It establishes the default template for the given column.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                                                      |                                                                                                                                                              |
| `displayRowIndexColumn()`              | <p>It makes visible or non-visible the column that shows the row identifier. You can also change the width of this column.<br></p><p><strong>Params</strong>:</p><ol><li>visible: <mark style="color:green;"><code>boolean</code></mark>.</li><li>width: <mark style="color:green;"><code>number</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                |                                                                                                                                                              |
| `changeColumnGroupStatus()`            | <p>It sets the given status to the given column group.<br></p><p><strong>Params</strong>:</p><ol><li>columnGroup: <mark style="color:green;"><code>DkGroupColumnDef</code></mark>.</li><li>status: <mark style="color:green;"><code>DisplayStatus</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                                               |                                                                                                                                                              |
| `setColumnSortable<R>()`               | <p>It makes <strong>sortable</strong> or <strong>non-sortable</strong> the given column.<br></p><p><strong>Params</strong>:</p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li><li>isSortable: <mark style="color:green;"><code>boolean</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                              |                                                                                                                                                              |
| `getColumns()`                         | <p>It returns all the existing columns.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>DkColumnDef\[]</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |                                                                                                                                                              |
| `getLinkedColumns()`                   | <p>It returns all the existing columns in a linked-node structure.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>DkNodeColumnDef\[]</code></mark>.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                            |                                                                                                                                                              |

## Events

| Event                        | Description                                                                                                                                                                                                                                                                                                                           |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `columnSizeChange`           | <p>It emits the column that has changed in width.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkDataColumnDef\<T, R>></code></mark>.</p>                                                                                                                  |
| `columnGroupStatusChange`    | <p>It emits the column group that has changed its status. </p><p>It sends the column group until the leaves.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGroupColumnDef></code></mark>.</p>                                                             |
| `columnLockedChange`         | <p>It emits the locked column for moving.</p><p>For column groups, it sends the column group until the leaves.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnDef></code></mark>.</p>                                                                   |
| `columnUnLockedChange`       | <p>It emits the unlocked column for moving.</p><p>For column groups, it sends the column group until the leaves.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnDef></code></mark>.</p>                                                                 |
| `columnVisibleChange`        | <p>It emits the column that changes its visibility.</p><p>For column groups, it sends the column group until the leaves.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnDef></code></mark>.</p>                                                         |
| `columnMove`                 | <p>It emits an object of type <mark style="color:green;"><code>DkColumnMoveEvent</code></mark>. In this object is specified the type of movement and the columns involved.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnMoveEvent></code></mark>.</p> |
| `columnsChange`              | <p>It emits the current columns from all panels. DkGrid emits this event every time a column operation occurs.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnDef\[]></code></mark>.</p>                                                                |
| `contentPanelsColumnsChange` | <p>It emits the current columns from the content panels. DkGrid emits this event every time a column operation occurs.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnDef\[]></code></mark>.</p>                                                        |
| `columnsRemovedChange`       | <p>DkGrid emits this event when you delete a column.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnDef\[]></code></mark>.</p>                                                                                                                          |


# DkGridSorting

You can access this instance through the property named sorting of the DkGridComponent.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.&#x20;

<mark style="color:green;">`<R>`</mark> represents the column data type: <mark style="color:green;">`string`</mark>, <mark style="color:green;">`number`</mark>, <mark style="color:green;">`Date`</mark>, or <mark style="color:green;">`boolean`</mark>.

## Methods

| Method                 | Description                                                                                                                                                                                                                                                                                    |                                                                                                          |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `sortBy()`             | <p>It sorts the DkGrid rows by the given column in the given direction.</p><p></p><p><strong>Params</strong>: </p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li><li>direction: <mark style="color:red;"><code>'asc'</code></mark>                 | <mark style="color:red;"><code>'desc'</code></mark></li></ol><p><strong>Return value</strong>: void.</p> |
| `isDkGridSortedBy()`   | <p>It evaluates if the DkGrid is sorted by the given column.</p><p></p><p><strong>Params</strong>: </p><ol><li>column: <mark style="color:green;"><code>DkDataColumnDef\<T, R></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>boolean</code></mark>.</p> |                                                                                                          |
| `getCurrentSortable()` | <p>It returns the column and the direction of the sorting operation. Otherwise, it returns null.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>DkColumnSorted\<T, R></code></mark>.</p>                                          |                                                                                                          |
| `clearSorting()`       | <p>It removes the current sorting operation.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                               |                                                                                                          |

## Events

| Event        | Description                                                                                                                                                                                                                                 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sortChange` | <p>It emits the column and the direction of the current sorting operation.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnSortEvent\<T></code></mark>.</p> |


# DkGridSelection

You can access this instance through the property named selection of the DkGridComponent.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

## Properties

| Property         | Description                                                                                                                                                             |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selectableRows` | <p>It gets or sets the function to define the selectable rows.</p><p></p><p><strong>Type</strong>: <mark style="color:green;"><code>SelectableFn\<T></code></mark>.</p> |

## Methods

| Method                 | Description                                                                                                                                                                                                                                                             |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selectOne()`          | <p>It marks the given row as selected.</p><p></p><p><strong>Params</strong>: </p><ol><li>row: <mark style="color:green;"><code>DkGridDataRowEntry\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>      |
| `select()`             | <p>It marks the given rows as selected.</p><p></p><p><strong>Params</strong>: </p><ol><li>rows: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p> |
| `deselectOne()`        | <p>It unchecks the given row as selected.</p><p></p><p><strong>Params</strong>: </p><ol><li>row: <mark style="color:green;"><code>DkGridDataRowEntry\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>   |
| `deselect()`           | <p>It unchecks the given rows as selected.</p><p></p><p><strong>Params</strong>: </p><ol><li>rows: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</li></ol><p><strong>Return</strong>: void.</p>                                             |
| `getSelectedRows()`    | <p>It returns all the selected rows.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</p>                                                                           |
| `getSelectedEntries()` | <p>It returns all the selected entries without the <mark style="color:green;"><code>DkGridDataRowEntry</code></mark> wrapper.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>T\[]</code></mark>.</p>       |
| `selectAll()`          | <p>It marks all the selectable rows coming from the data source.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                    |
| `deselectAll()`        | <p>It unchecks all the selected rows.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                               |

## Events

| Event                 | Description                                                                                                                                                                                              |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selectedRowChange`   | <p>It emits the selected row.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>></code></mark>.</p>         |
| `deselectedRowChange` | <p>It emits the deselected row.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>></code></mark>.</p>       |
| `selectionChange`     | <p>It emits all the selected rows.</p><p></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>\[]></code></mark>.</p> |


# DkGridRowGrouping

You can access this instance through the property named rowGrouping of the DkGridComponent.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

## Methods

| Method               | Description                                                                                                                                                                                            |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `clearRowGrouping()` | <p>It moves all the columns from the group panel to the center panel.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p> |

## Events

| Event                     | Description                                                                                                                                                                                                                                                                                          |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `groupPanelColumnsChange` | <p>It emits when the user moves a column to, from, or inside the group panel. This event sends all the resulting columns after the movement.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkColumnDef\[]></code></mark>.</p> |
| `collapseGroupRowChange`  | <p>It emits when the user opens or closes a group row.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGroupRowCollapseEvent\<T>></code></mark>.</p>                                                                          |


# DkGridPagination

You can access this instance through the property named pagination of the DkGridComponent.

## Properties

| Property       | Description                                                                                                                                                                             |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `matPaginator` | <p>It is a <strong>read-only</strong> property and returns the MatPaginator instance.<br></p><p><strong>Type</strong>: <mark style="color:green;"><code>MatPaginator</code></mark>.</p> |

## Events

| Event             | Description                                                                                                                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `pageChange`      | <p>It emits when the user navigates through the available pages.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<PageEvent></code></mark>.</p>            |
| `paginatorChange` | <p>It emits when the user allows or disallows the DikeGrid's pagination.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<MatPaginator></code></mark>.</p> |


# DkGridWaitingIndicator

You can access this instance through the property called waitingIndicator of the DkGridComponent.

## Methods

| Method                     | Description                                                                                                                                                                                                                                                                                                 |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `loadingIndicator()`       | <p>It displays the <strong>Loading Indicator</strong> showing the current loading message.</p><p><br><strong>Params</strong>: None.<br><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                    |
| `processingIndicator()`    | <p>It displays the <strong>Processing Indicator</strong> showing the current process message.<br><br><strong>Params</strong>: None.<br><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                    |
| `customMessageIndicator()` | <p>It displays the <strong>Waiting</strong> <strong>Indicator</strong> showing the message it receives as an argument.<br><br><strong>Params</strong>: message: <mark style="color:green;"><code>string</code></mark>.<br><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p> |
| `hideWaitingIndicator()`   | <p>It hides any of the previous Waiting indicators.<br><br><strong>Params</strong>: None.<br><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                              |


# DkGridFactoryDataSource

You can access this instance through the property named dataSourceFactory of the DkGridComponent.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

## Methods

| Method                       | Description                                                                                                                                                                                                                                                                                                                        |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `createFilteringDecorator()` | <p>It wraps the given decorator into the filtering decorator.<br></p><p><strong>Params</strong>:</p><ol><li>dkDataSource: <mark style="color:green;"><code>DkDataSource\<DkGridDataRowEntry\<T>></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>DkFilteringDataSource\<T></code></mark>.</p> |
| `createSortingDecorator()`   | <p>It wraps the given decorator into the sorting decorator.<br></p><p><strong>Params</strong>:</p><ol><li>dkDataSource: <mark style="color:green;"><code>DkDataSource\<DkGridDataRowEntry\<T>></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>DkSortingDataSource\<T></code></mark>.</p>     |


# DkGridFilter

You can access this instance through the property named filter of the DkGridComponent.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

## Properties

| Property      | Description                                                                                                                                                                                                         |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filterables` | <p>It is a <strong>read-only</strong> property that returns all the defined filterables.<br></p><p><strong>Type</strong>: </p><p><mark style="color:green;"><code>Map\<string, DkFilterable\<T>></code></mark>.</p> |

## Methods

| Method              | Description                                                                                                                                                                                                            |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clearFilter()`     | <p>It removes all conditions from filterables.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                        |
| `isFilterApplied()` | <p>It evaluates if the DkGrid instance has one filter condition applied, at least.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>boolean</code></mark>.</p> |
| `getFilteredRows()` | <p>It returns all rows that meet the filter conditions criteria.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</p> |

## Events

| Event                        | Description                                                                                                                                                                                                                                                                                                                                                                             |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `registerFilterableChange`   | <p>It emits when you define a column as <strong>filterable</strong>. Then, the DkGrid instance will add the column to the internal Map named <em><strong>filterables</strong></em>.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkFilterable\<T>></code></mark>.</p>                                           |
| `deregisterFilterableChange` | <p>It emits when you make a column <strong>not filterable</strong> when it was <strong>filterable</strong>. Then, the DkGrid instance will remove the column to the internal Map named <em><strong>filterables</strong></em>.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkFilterable\<T>></code></mark>.</p> |
| `filterChange`               | <p>It emits when the user has <strong>defined/changed</strong> a filter condition.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkFilterable\<T>></code></mark>.</p>                                                                                                                                            |
| `editionFilterChange`        | <p>It emits when the user has <strong>defined/changed</strong> an edition filter condition.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkFilterable\<T>></code></mark>.</p>                                                                                                                                   |
| `clearFilterChange`          | <p>It emits after the DkGrid instance has <strong>removed</strong> all the filter conditions.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<void></code></mark>.</p>                                                                                                                                             |
| `clearEditionFilterChange`   | <p>It emits after the DkGrid instance has <strong>removed</strong> all the filter conditions from the edition <em><strong>filterables</strong></em>.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<void></code></mark>.</p>                                                                                      |


# DkGridEdition

You can access this instance through the property named edition of the DkGridComponent.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

## Properties

| Property             | Description                                                                                                                                                                                                                                           |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `rowsInEdition`      | <p>It is a <strong>read-only</strong> property that returns all the rows in edition state.<br></p><p><strong>Type</strong>: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</p>                                             |
| `modifiedRows`       | <p>It is a <strong>read-only</strong> property that returns all the modified rows.<br></p><p><strong>Type</strong>: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</p>                                                     |
| `removedRows`        | <p>It is a <strong>read-only</strong> property that returns all the deleted rows.<br></p><p><strong>Type</strong>: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</p>                                                      |
| `editionFilterState` | <p>It is a <strong>read-only</strong> property that returns <mark style="color:red;"><code>true</code></mark> if the user navigates the edition filters.<br></p><p><strong>Type</strong>: <mark style="color:green;"><code>boolean</code></mark>.</p> |

## Methods

| Method                  | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `editRow()`             | <p>It changes the row status to <strong>edition state</strong>.<br></p><p><strong>Params</strong>: </p><ol><li>row: <mark style="color:green;"><code>DkGridDataRowEntry\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                 |
| `updateRow()`           | <p>It <strong>saves</strong> the changes in the given row. The given row must be <strong>valid</strong> and <strong>not pristine</strong>. After updating, DkGrid sets the row status to <mark style="color:red;"><code>Modified</code></mark>.<br></p><p><strong>Params</strong>: </p><ol><li>row: <mark style="color:green;"><code>DkGridDataRowEntry\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p> |
| `cancelRowEdition()`    | <p>It changes the row status to <mark style="color:red;"><code>Read</code></mark> status. This method discards all the changes in the given row.<br></p><p><strong>Params</strong>: </p><ol><li>row: <mark style="color:green;"><code>DkGridDataRowEntry\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                |
| `removeRow()`           | <p>It changes the row status to <mark style="color:red;"><code>Deleted</code></mark> status.<br></p><p><strong>Params</strong>: </p><ol><li>row: <mark style="color:green;"><code>DkGridDataRowEntry\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                    |
| `restoreRow()`          | <p>It reverts any change that the user has made.<br></p><p><strong>Params</strong>: </p><ol><li>row: <mark style="color:green;"><code>DkGridDataRowEntry\<T></code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                |
| `updateRowsInEdition()` | <p>It takes all <strong>valid</strong> rows which are <strong>not</strong> <strong>pristine</strong> and saves their changes.<br></p><p><strong>Params</strong>: None</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                            |
| `removeRows()`          | <p>It deletes the given rows that are not in the edition state.<br></p><p><strong>Params</strong>: </p><ol><li>rows: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                             |
| `cancelRowsInEdition()` | <p>It cancels the current edition state for every row, losing all the changes you have made.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                            |
| `restoreRows()`         | <p>It takes those <strong>modified</strong> or <strong>removed</strong> rows from the given rows and restores them to their immediate previous state.<br></p><p><strong>Params</strong>: </p><ol><li>rows: <mark style="color:green;"><code>DkGridDataRowEntry\<T>\[]</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                       |
| `setEditionFilter()`    | <p>It displays the corresponding subset of rows editing.<br></p><p><strong>Params</strong>: </p><ol><li>value: <mark style="color:green;"><code>RowStatus</code></mark>.</li></ol><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                   |
| `cancelEditionFilter()` | <p>It makes DkGrid shows the standard view.<br></p><p><strong>Params</strong>: None</p><p><strong>Return</strong>: <mark style="color:green;"><code>void</code></mark>.</p>                                                                                                                                                                                                                                                                                              |

## Events

| Event                      | Description                                                                                                                                                                                                                     |                                               |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `editionRowChange`         | <p>It emits the row that has been changed to edition mode.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>></code></mark>.</p>      |                                               |
| `updateRowChange`          | <p>It emits the modified rows.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>                                                      | DkGridDataRowEntry\<T>\[]></code></mark>.</p> |
| `cancelRowEditionChange`   | <p>It emits the rows that have been canceled for edition.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>                           | DkGridDataRowEntry\<T>\[]></code></mark>.</p> |
| `removeRowChange`          | <p>It emits the rows that have been deleted.</p><p><br><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>                                        | DkGridDataRowEntry\<T>\[]></code></mark>.</p> |
| `removeRowChange`          | <p>It emits the rows that have been deleted.</p><p><br><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>                                        | DkGridDataRowEntry\<T>\[]></code></mark>.</p> |
| `restoreRowChange`         | <p>It emits the rows whose changes were reverted.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<DkGridDataRowEntry\<T>                                   | DkGridDataRowEntry\<T>\[]></code></mark>.</p> |
| `editionFilterStateChange` | <p>It emits every time you navigate between the standard and the edition view.<br></p><p><strong>Params</strong>: None.</p><p><strong>Return</strong>: <mark style="color:green;"><code>Observable\<boolean></code></mark>.</p> |                                               |


# Components


# DkGridComponent

This class creates a DkGrid instance through the HTML definition.

## Component metadata

selector: <mark style="color:red;">`dk-grid`</mark>

export as: <mark style="color:red;">`dkgGrid`</mark>

## Class

<mark style="color:green;">`DkGridComponent<T>`</mark>

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

## Definition

```typescript
class DkGridComponent<T extends object> implements OnInit, OnChanges, AfterContentInit, AfterViewInit, OnDestroy {

  @HostBinding('attr.id')
  @Input() id: string;

  @Input() height: string;
  @Input() width: string;

  @Input()
  get displayRowId(): boolean;
  set displayRowId(value: any);
  static ngAcceptInputType_displayRowId: BooleanInput;

  @Input()
  get rowIdWidth(): number;
  set rowIdWidth(value: any);
  static ngAcceptInputType_rowIdWidth: NumberInput;

  @Input()
  get gridElevation(): boolean;
  set gridElevation(value: any);
  static ngAcceptInputType_gridElevation: BooleanInput;

  @Input()
  get gridElevationValue(): number;
  set gridElevationValue(value: any);
  static ngAcceptInputType_gridElevationValue: NumberInput;

  @Input()
  get verticalRowLines(): boolean;
  set verticalRowLines(value: any);
  static ngAcceptInputType_verticalRowLines: BooleanInput;

  @Input()
  get striped(): boolean;
  set striped(value: any);
  static ngAcceptInputType_striped: BooleanInput;

  @Input()
  get headerRowHeight(): number;
  set headerRowHeight(value: any);
  static ngAcceptInputType_headerRowHeight: NumberInput;

  @Input()
  get contentRowHeight(): number;
  set contentRowHeight(value: any);
  static ngAcceptInputType_contentRowHeight: NumberInput;

  @Input()
  get allowRowGrouping(): boolean;
  set allowRowGrouping(value: any);
  static ngAcceptInputType_allowRowGrouping: BooleanInput;

  @Input()
  get rowGroupingRowHeight(): number;
  set rowGroupingRowHeight(value: any);
  static ngAcceptInputType_rowGroupingRowHeight: NumberInput;

  @Input()
  get rowGroupingColumnHeight(): number;
  set rowGroupingColumnHeight(value: any);
  static ngAcceptInputType_rowGroupingColumnHeight: NumberInput;

  @Input()
  get displayRowGroupingIndent(): boolean;
  set displayRowGroupingIndent(value: any);
  static ngAcceptInputType_displayRowGroupingIndent: BooleanInput;

  @Input()
  get rowGroupingIndentWidth(): number;
  set rowGroupingIndentWidth(value: any);
  static ngAcceptInputType_rowGroupingIndentWidth: NumberInput;

  // Row Grouping events:
  @Output() readonly groupPanelColumnsChange: EventEmitter<DkColumnDef[]>;
  @Output() readonly collapseGroupRowChange: EventEmitter<DkGroupRowCollapseEvent<T>>;

  @Input()
  get allowPagination(): boolean;
  set allowPagination(value: any);
  static ngAcceptInputType_allowPagination: BooleanInput;

  @Input()
  get pageSize(): number;
  set pageSize(value: any);
  static ngAcceptInputType_pageSize: NumberInput;

  @Input()
  get paginationRowHeight(): number;
  set paginationRowHeight(value: any);
  static ngAcceptInputType_paginationRowHeight: NumberInput;

  // Pagination events:
  @Output() readonly pageChange: EventEmitter<PageEvent>;
  @Output() readonly paginatorChange: EventEmitter<MatPaginator>;

  @Input()
  get onDemandFilter(): boolean;
  set onDemandFilter(value: any);
  static ngAcceptInputType_onDemandFilter: BooleanInput;

  @Input()
  get allowRowFiltering(): boolean;
  set allowRowFiltering(value: any);
  static ngAcceptInputType_allowRowFiltering: BooleanInput;

  @Input()
  get filterRowHeight(): number;
  set filterRowHeight(value: any);
  static ngAcceptInputType_filterRowHeight: NumberInput;

  @Input() gridCustomFilterConditions: DkGridCustomFilterConditions<T>;

  // Filter events:
  @Output() readonly registerFilterableChange: EventEmitter<DkFilterable<T>>;
  @Output() readonly deregisterFilterableChange: EventEmitter<DkFilterable<T>>;
  @Output() readonly filterChange: EventEmitter<DkFilterable<T>>;
  @Output() readonly editionFilterChange: EventEmitter<DkFilterable<T>>;
  @Output() readonly clearFilterChange: EventEmitter<void>;
  @Output() readonly clearEditionFilterChange: EventEmitter<void>;

  @Input()
  get allowEdition(): boolean;
  set allowEdition(value: any);
  static ngAcceptInputType_allowEdition: BooleanInput;

  @Input()
  get editionMode(): boolean;
  set editionMode(value: any);
  static ngAcceptInputType_editionMode: BooleanInput;

  @Input()
  get editionToolbar(): boolean;
  set editionToolbar(value: any);
  static ngAcceptInputType_editionToolbar: BooleanInput;

  @Input() editionToolbarPosition: EditionToolbarPosition;
  @Input() editionToolbarAlignment: EditionToolbarAlignment;

  @Input()
  get editionToolbarRowHeight(): number;
  set editionToolbarRowHeight(value: any);
  static ngAcceptInputType_editionToolbarRowHeight: NumberInput;

  @Input()
  get editionToolbarItemHeight(): number;
  set editionToolbarItemHeight(value: any);
  static ngAcceptInputType_editionToolbarItemHeight: NumberInput;

  @Input() gridEditionSettings: DkGridEditionSettings<T>;

  // Edition events:
  @Output() readonly editionRowChange: EventEmitter<DkGridDataRowEntry<T>>;
  @Output() readonly updateRowChange: EventEmitter<DkGridDataRowEntry<T> | DkGridDataRowEntry<T>[]>;
  @Output() readonly cancelRowEditionChange: EventEmitter<DkGridDataRowEntry<T> | DkGridDataRowEntry<T>[]>;
  @Output() readonly removeRowChange: EventEmitter<DkGridDataRowEntry<T> | DkGridDataRowEntry<T>[]>;
  @Output() readonly restoreRowChange: EventEmitter<DkGridDataRowEntry<T> | DkGridDataRowEntry<T>[]>;
  @Output() readonly editionFilterStateChange: EventEmitter<boolean>;

  @Input()
  get allowSelection(): boolean;
  set allowSelection(value: any);
  static ngAcceptInputType_allowSelection: BooleanInput;

  @Input() selectableRows: SelectableFn<T>;

  // Selection events:
  @Output() readonly selectionChange: EventEmitter<DkGridDataRowEntry<T>[]>;
  @Output() readonly selectedRowChange: EventEmitter<DkGridDataRowEntry<T>>;
  @Output() readonly deselectedRowChange: EventEmitter<DkGridDataRowEntry<T>>;

  @Input() datasource: DkGridDataSourceInput<T>;
  @Input() decoratorFn: DataSourceDecoratorFn<T>;

  // DataSource events:
  @Output() readonly dataDeliveryIdChange: EventEmitter<string>;

  @Input()
  get allowColumnContextMenu(): boolean;
  set allowColumnContextMenu(value: any);
  static ngAcceptInputType_allowColumnContextMenu: BooleanInput;

  @Input()
  get allowColumnDragging(): boolean;
  set allowColumnDragging(value: any);
  static ngAcceptInputType_allowColumnDragging: BooleanInput;

  // DkGridColumnDef service:
  @Output() readonly gridColumnDefInstance: EventEmitter<DkGridColumnDef<T>>;

  @Input()
  get allowSorting(): boolean;
  set allowSorting(value: any);
  static ngAcceptInputType_allowSorting: BooleanInput;

  // Sorting event:
  @Output() readonly sortChange: EventEmitter<DkColumnSortEvent<T>>;

  // Column events:
  @Output() readonly columnSizeChange: EventEmitter<DkDataColumnDef<T, string | number | Date | boolean>>;
  @Output() readonly columnGroupStatusChange: EventEmitter<DkGroupColumnDef>;
  @Output() readonly columnLockedChange: EventEmitter<DkColumnDef>;
  @Output() readonly columnUnLockedChange: EventEmitter<DkColumnDef>;
  @Output() readonly columnVisibleChange: EventEmitter<DkColumnDef>;
  @Output() readonly columnMove: EventEmitter<DkColumnMoveEvent>;
  @Output() readonly columnsChange: EventEmitter<DkColumnDef[]>;
  @Output() readonly contentPanelsColumnsChange: EventEmitter<DkColumnDef[]>;
  @Output() readonly columnsRemovedChange: EventEmitter<DkColumnDef[]>;

  // Public properties:
  readonly filter: DkGridFilter<T>;
  readonly columnDef: DkGridColumnDef<T>;
  readonly edition: DkGridEdition<T>;
  readonly selection: DkGridSelection<T>;
  readonly sorting: DkGridSorting<T>;
  readonly pagination: DkGridPagination;
  readonly rowGrouping: DkGridRowGrouping<T>;
  readonly dataSourceFactory: DkGridFactoryDataSource<T>;
  readonly waitingIndicator: DkGridWaitingIndicator;

  get initialized(): boolean;
}
```


# DkGridColumnComponent

This class creates a column, either a data column or a group column, using the HTML definition.

## Component metadata

selector: <mark style="color:red;">`dk-grid-column`</mark>

export as: <mark style="color:red;">`dkgColumn`</mark>

## Class

<mark style="color:green;">`DkGridColumnComponent<T, R>`</mark>

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

<mark style="color:green;">`<R>`</mark> represents the column data type: <mark style="color:green;">`string`</mark>, <mark style="color:green;">`number`</mark>, <mark style="color:green;">`Date`</mark>, or <mark style="color:green;">`boolean`</mark>.

## Definition

```typescript
class DkGridColumnComponent<T extends object, R extends string | number | Date | boolean> implements AfterContentInit {

  slotId: string;

  @Input() fieldName: string;
  @Input() headerText: string;
  @Input() headerTooltip: string;
  @Input() dataType: DkColumnDataType;

  @Input()
  get order(): number;
  set order(value: any);
  static ngAcceptInputType_order: NumberInput;

  @Input()
  get width(): number;
  set width(value: any);
  static ngAcceptInputType_width: NumberInput;

  @Input()
  get minWidth(): number;
  set minWidth(value: any);
  static ngAcceptInputType_minWidth: NumberInput;

  @Input()
  get maxWidth(): number;
  set maxWidth(value: any);
  static ngAcceptInputType_maxWidth: NumberInput;

  @Input() contentAlign: ColumnAlignment;

  @Input()
  get draggable(): boolean;
  set draggable(value: any);
  static ngAcceptInputType_draggable: BooleanInput;

  @Input()
  get locked(): boolean;
  set locked(value: any);
  static ngAcceptInputType_locked: BooleanInput;

  @Input()
  get resizable(): boolean;
  set resizable(value: any);
  static ngAcceptInputType_resizable: BooleanInput;

  @Input()
  get visible(): boolean;
  set visible(value: any);
  static ngAcceptInputType_visible: BooleanInput;

  @Input()
  get sortable(): boolean;
  set sortable(value: any);
  static ngAcceptInputType_sortable: BooleanInput;

  @Input()
  get filterable(): boolean;
  set filterable(value: any);
  static ngAcceptInputType_filterable: BooleanInput;

  @Input()
  get editable(): boolean;
  set editable(value: any);
  static ngAcceptInputType_editable: BooleanInput;

  @Input()
  get groupable(): boolean;
  set groupable(value: any);
  static ngAcceptInputType_groupable: BooleanInput;

  @Input()
  get displayMenu(): boolean;
  set displayMenu(value: any);
  static ngAcceptInputType_displayMenu: BooleanInput;

  @Input() filter: DkFilter[];
  @Input() customFilterConditions: CustomFilterConditionInstance<T>;

  @Input() panel: DkGridPanel;

  @Input() displayOn: DisplayStatus;
  @Input() displayStatus: DisplayStatus;

  @Input() getValue: GetterFn<T, R>;
  @Input() setValue: SetterFn<T, R>;

  @Input() editionSettings: EditionFieldSettings;
  @Input() editionTemplate: TemplateRef<any> | null | undefined;
  @Input() displayTemplate: TemplateRef<any> | null | undefined;
}
```


# Classes


# DataSource

\<T> represents the object's shape coming from the data source.

## DikeDataSource\<T>

```typescript
abstract class DikeDataSource<T> extends DataSource<T> {

  constructor();
  
  abstract get data(): T[];
  abstract set data(value: T[]);

  abstract connect(): Observable<T[]>;
  abstract disconnect(): void;
  abstract reconnect(): void;

  abstract dispose(): void;
}
```

## DikeGridDataSource\<T>

```typescript
class DikeGridDataSource<T extends object> extends DikeDataSource<DikeGridDataRowEntry<T>> {

  constructor();

  get dataDeliveryChanges(): Observable<string>;
  get data(): DikeGridDataRowEntry<T>[];
  set data(value: DikeGridDataRowEntry<T>[]);
  get entries(): ReadonlyArray<T>;
  set entries(value: ReadonlyArray<T>);

  connect(): Observable<DikeGridDataRowEntry<T>[]>;
  disconnect(): void;
  reconnect(): void;
  dispose(): void;
}
```

## DikeDecoratorDataSource\<T>

```typescript
abstract class DikeDecoratorDataSource<T extends object> extends DikeDataSource<DikeGridDataRowEntry<T>> {

  constructor(
    protected dikeDataSource: DikeDataSource<DikeGridDataRowEntry<T>>);

  get data(): DikeGridDataRowEntry<T>[];
  set data(value: DikeGridDataRowEntry<T>[]);

  connect(): Observable<DikeGridDataRowEntry<T>[]>;
  disconnect(): void;
  reconnect(): void;
  dispose(): void;

  protected abstract setChangeSubscription(): void;
}

```


# Columns

\<T> represents the object's shape coming from the data source. \<R> represents the column data type: string, number, Date, or boolean.

## DikeColumnDef

```typescript
abstract class DikeColumnDef {
  slotId: string | null;
  belongToGroup: string | null;

  columnId: string | null;

  get fieldName(): string {
    return this._fieldName;
  }
  protected _fieldName: string;

  get headerText(): string {
    return this._headerText;
  }
  protected _headerText: string;

  order: number;

  headerTooltip: string;
  resizable: boolean;
  draggable: boolean;
  visible: boolean;
  displayMenu: boolean;

  panel: DikeGridPanel;

  displayOn: DisplayStatus;

  constructor(fieldName: string, headerText: string);
  assign(column: DikeColumnDef): this;
}
```

## DikeGroupColumnDef

```typescript
class DikeGroupColumnDef extends DikeColumnDef {
  children: DikeColumnDef[];
  displayStatus: DisplayStatus;

  constructor(fieldName: string, headerText: string);
  assign(column: DikeGroupColumnDef): this;
}
```

## DikeDataColumnDef\<T, R>

```typescript
abstract class DikeDataColumnDef<T extends object, R extends string | number | Date | boolean> extends DikeColumnDef {
  get dataType(): DikeColumnDataType {
    return this._dataType;
  }
  protected _dataType: DikeColumnDataType;

  width: number;
  minWidth: number;
  maxWidth: number;
  contentAlign: ColumnAlignment;

  sortable: boolean;
  filterable: boolean;
  editable: boolean;
  groupable: boolean;

  abstract get filter(): DikeFilter[];
  abstract set filter(value: DikeFilter[]);
  
  abstract get customFilterConditions(): CustomFilterConditionInstance<T>;
  abstract set customFilterConditions(value : CustomFilterConditionInstance<T>);

  editionSettings: EditionFieldSettings;
  editionTemplate: Observable<TemplateRef<any> | null> | TemplateRef<any> | null;
  displayTemplate: Observable<TemplateRef<any> | null> | TemplateRef<any> | null;

  constructor(fieldName: string, headerText: string, dataType: DikeColumnDataType);
  assign(column: DikeDataColumnDef<T, R>): this;
  static getDataColumnDefInstance<T extends object>(fieldName: string, headerText: string, dataType: DikeColumnDataType): DikeDataColumnDef<T, string | number | Date | boolean>;

  abstract getValue: GetterFn<T, R>;
  abstract setValue: SetterFn<T, R>;
}
```

## DikeTextColumnDef\<T>

```typescript
class DikeTextColumnDef<T extends object> extends DikeDataColumnDef<T, string> {
  constructor(fieldName: string, headerText: string);
  
  get filter(): DikeTextFilter[] | DikeTextCaseFilter[];
  set filter(value: DikeTextFilter[] | DikeTextCaseFilter[]);
  
  get customFilterConditions(): CustomTextCaseFilterCondition<T> | CustomMultipleTextFilterCondition<T>;
  set customFilterConditions(value : CustomTextCaseFilterCondition<T> | CustomMultipleTextFilterCondition<T>);

  getValue: GetterFn<T, string | null | undefined> = (entry: T): string => {
    return ((entry as { [key in keyof T]: T[key] })[this.fieldName]) as string;
  };

  setValue: SetterFn<T, string> = (entry: T, value: string): void => {
    (entry as { [key in keyof T]: T[key] })[this.fieldName] = value;
  };
}
```

## DikeNumericColumnDef\<T>

```typescript
class DikeNumericColumnDef<T extends object> extends DikeDataColumnDef<T, number> {
  constructor(fieldName: string, headerText: string);

  get filter(): DikeNumericFilter[];
  set filter(value: DikeNumericFilter[]);
  
  get customFilterConditions(): CustomNumericFilterCondition<T> | CustomMultipleNumericFilterCondition<T>;
  set customFilterConditions(value : CustomNumericFilterCondition<T> | CustomMultipleNumericFilterCondition<T>);
  
  getValue: GetterFn<T, number> = (entry: T): number => {
    return ((entry as { [key in keyof T]: T[key] })[this.fieldName]) as number;
  };

  setValue: SetterFn<T, number> = (entry: T, value: number): void => {
    (entry as { [key in keyof T]: T[key] })[this.fieldName] = value;
  };
}
```

## DikeDateColumnDef\<T>

```typescript
class DikeDateColumnDef<T extends object> extends DikeDataColumnDef<T, string | number | Date> {
  constructor(fieldName: string, headerText: string);

  get filter(): DikeDateFilter[];
  set filter(value: DikeDateFilter[]);
  
  get customFilterConditions(): CustomDateFilterCondition<T>;
  set customFilterConditions(value : CustomDateFilterCondition<T>);

  getValue: GetterFn<T, string | number | Date> = (entry: T): string | number | Date => {
    return ((entry as { [key in keyof T]: T[key] })[this.fieldName]) as string | number | Date;
  };

  setValue: SetterFn<T, string | number | Date> = (entry: T, value: string | number | Date): void => {
    (entry as { [key in keyof T]: T[key] })[this.fieldName] = value;
  };
}
```

## DikeBinaryColumnDef\<T>

```typescript
class DikeBinaryColumnDef<T extends object> extends DikeDataColumnDef<T, string | number | boolean> {
  constructor(fieldName: string, headerText: string);

  get filter(): DikeBinaryFilter[];
  set filter(value: DikeBinaryFilter[]);
  
  get customFilterConditions(): CustomBinaryFilterCondition<T>;
  set customFilterConditions(value : CustomBinaryFilterCondition<T>);
  
  getValue: GetterFn<T, string | number | boolean> = (entry: T): string | number | boolean => {
    return ((entry as { [key in keyof T]: T[key] })[this.fieldName]) as string | number | boolean;
  };

  setValue: SetterFn<T, string | number | boolean> = (entry: T, value: string | number | boolean): void => {
    (entry as { [key in keyof T]: T[key] })[this.fieldName] = value;
  };
}
```


# Rows

\<T> represents the object's shape coming from the data source.

## DikeGridRowEntry

```typescript
abstract class DikeGridRowEntry {
  isGroup: boolean;
  
  constructor(timestampCreation: number);

  get selectedChange(): Observable<boolean>;
  get timestampChange(): Observable<number>;
  
  get timestamp(): number;
  set timestamp(value: number);

  get selected(): boolean;
  set selected(value: boolean);
}

```

## DikeGridGroupRowEntry\<T>

```typescript
class DikeGridGroupRowEntry<T extends object> extends DikeGridRowEntry {

  constructor(
    private groupKey: unknown,
    private groupId: string,
    private groupLevel: number,
    private groupTotalCount: number,
    private dataColumnDef: DikeDataColumnDef<T, string | number | Date | boolean>,
    private dataRowEntries: DikeGridDataRowEntry<T>[],
    timestamp: number);

  get collapseChange(): Observable<boolean>;
  get allRowsSelectedChange(): Observable<boolean>;
  get someRowsSelectedChange(): Observable<boolean>;

  get key(): unknown;
  get id(): string;
  get level(): number;
  get count(): number;
  set count(value: number);
  get totalCount(): number;
  get totalSelectableRows(): number;
  set totalSelectableRows(value: number);
  get rowSelectionCount(): number;
  set rowSelectionCount(value: number);
  get collapse(): boolean;
  set collapse(value: boolean);
  get allRowsSelected(): boolean;
  set allRowsSelected(value: boolean);
  get someRowsSelected();
  set someRowsSelected(value: boolean);
  get dataColumn(): DikeDataColumnDef<T, string | number | Date | boolean>;
  get dataRows(): DikeGridDataRowEntry<T>[];
}
```

## DikeGridDataRowEntry\<T>

```typescript
class DikeGridDataRowEntry<T extends object> extends DikeGridRowEntry {

  rowFormFields: Map<string, RowFormField>;
  
  constructor(
    private rowId: number,
    timestamp: number,
    private rowEntry: T);
    
  get rowFormGroupChange(): Observable<FormGroup>;
  get editionActionsChange(): Observable<RowEditionActions>;
  get isEditingChange(): Observable<boolean>;
  
  get id(): number;
  get entry(): T;
  get status(): RowStatus;
  set status(value: RowStatus);
  get isEditing(): boolean;
  set isEditing(value: boolean);
  get rowFormGroup(): FormGroup;
  set rowFormGroup(value: FormGroup);
  
  getRowPanelFormGroup(panel: DikeContentPanel): FormGroup;
}

```


# Filtering

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

<mark style="color:green;">`<R>`</mark> represents the column data type: <mark style="color:green;">`string`</mark>, <mark style="color:green;">`number`</mark>, <mark style="color:green;">`Date`</mark>, or <mark style="color:green;">`boolean`</mark>.

<mark style="color:green;">`<V>`</mark> represents the filter values.

<mark style="color:green;">`<M>`</mark> represents the filter selection model: <mark style="color:green;">`DikeSelectionModel`</mark>.

## DikeFilterCondition\<T, R, V>

```typescript
abstract class DikeFilterCondition<T extends object, R extends string | number | Date | boolean, V> {
  protected indexConditions: FilterCondition<T, R, V>;

  constructor();

  get conditions(): Condition<T, R, V>[];

  assign(filterConditions: DikeFilterCondition<T, R, V>): this;  
  addCondition(condition: Condition<T, R, V>): this;
  removeCondition(name: string): Condition<T, R, V>;
  getCondition(name: string): Condition<T, R, V>;
}
```

## DikeSelectionFilterCondition\<M, T, R, V>

```typescript
abstract class DikeSelectionFilterCondition<M, T extends object, R extends string | number | Date | boolean, V> extends DikeFilterCondition<T, R, V> {
  protected dikeFilterOptions: M[];

  constructor();

  abstract set options(options: M[]);
  abstract get options(): M[];

  assign(value: DikeSelectionFilterCondition<M, T, R, V>): this;
  addCondition(condition: Condition<T, R, V>): this;

  protected isValidCondition(name: string): boolean;
}
```

## DikeBinarySelectionFilterCondition\<M, T, V>

```typescript
abstract class DikeBinarySelectionFilterCondition<M, T extends object, V> extends DikeSelectionFilterCondition<M, T, string | number | boolean, V> {
  private MAX_OPTIONS: number = 2;

  constructor();

  set options(options: M[]);
  get options(): M[];

  assign(value: DikeBinarySelectionFilterCondition<M, T, V>): this;
}
```

## CustomBinaryFilterCondition\<T>

```typescript
class CustomBinaryFilterCondition<T extends object> extends DikeBinarySelectionFilterCondition<DikeBinarySelectionModel, T, DikeBinaryFilter> {
  constructor();

  addExistingCondition(name: string): this;
}
```

## DikeMultipleSelectionFilterCondition\<M, T, R, V>

```typescript
abstract class DikeMultipleSelectionFilterCondition<M, T extends object, R extends string | number | boolean | Date, V> extends DikeSelectionFilterCondition<M, T, R, V> {
  constructor();

  set options(options: M[]);
  get options(): M[];
  
  assign(value: DikeSelectionFilterCondition<M, T, R, V>): this;
}
```

## CustomTextCaseFilterCondition\<T>

```typescript
class CustomTextCaseFilterCondition<T extends object> extends DikeFilterCondition<T, string, DikeTextCaseFilter> {
  constructor();

  addExistingCondition(name: string): this;
  protected isValidCondition(name: string): boolean;
}
```

## CustomRowTextCaseFilterCondition\<T>

```typescript
class CustomRowTextCaseFilterCondition<T extends object> extends CustomTextCaseFilterCondition<T> {
  constructor();

  addCondition(condition: Condition<T, string, DikeTextCaseFilter>): this;
}
```

## CustomNumericFilterCondition\<T>

```typescript
class CustomNumericFilterCondition<T extends object> extends DikeFilterCondition<T, number, DikeNumericFilter> {
  constructor();

  addExistingCondition(name: string): this;
  protected isValidCondition(name: string): boolean;
}
```

## CustomRowNumericFilterCondition\<T>

```typescript
class CustomRowNumericFilterCondition<T extends object> extends CustomNumericFilterCondition<T> {
  constructor();

  addCondition(condition: Condition<T, number, DikeNumericFilter>): this;
  protected isValidCondition(name: string): boolean;
}
```

## CustomDateFilterCondition\<T>

```typescript
class CustomDateFilterCondition<T extends object> extends DikeFilterCondition<T, string | number | Date, DikeDateFilter> {
  constructor();

  addExistingCondition(name: string): this;
  protected isValidCondition(name: string): boolean;
}
```

## CustomRowDateFilterCondition\<T>

```typescript
class CustomRowDateFilterCondition<T extends object> extends CustomDateFilterCondition<T> {
  constructor();

  addCondition(condition: Condition<T, string | number | Date, DikeDateFilter>): this;
  protected isValidCondition(name: string): boolean;
}
```

## CustomMultipleTextFilterCondition\<T>

```typescript
class CustomMultipleTextFilterCondition<T extends object> extends DikeMultipleSelectionFilterCondition<DikeTextSelectionModel, T, string, DikeTextFilter>  {
  constructor();
    
  addExistingCondition(name: string): this
}
```

## CustomMultipleNumericFilterCondition\<T>

```typescript
class CustomMultipleNumericFilterCondition<T extends object> extends DikeMultipleSelectionFilterCondition<DikeNumericSelectionModel, T, number, DikeNumericFilter>  {

  constructor();
  
  addExistingCondition(name: string): this;
}
```


# Editing

## DikeErrorMessage

```typescript
abstract class DikeErrorMessage {
  constructor();
  
  get messages(): string[];
  
  assign(messages: DikeErrorMessage): this;
  addMessage(errorKey: string, errorMessage: string): this;
  removeMessage(errorKey: string): string | undefined;
  getMessage(errorKey: string): string | undefined;
}
```

## DikeStandardErrorMessage

```typescript
class DikeStandardErrorMessage extends DikeErrorMessage {
  constructor() {
    super();

    this.addMessage(ErrorType.MIN, 'The minimum number is incorrect');
    this.addMessage(ErrorType.MAX, 'The maximum number is incorrect');
    this.addMessage(ErrorType.REQUIRED, 'Required field');
    this.addMessage(ErrorType.EMAIL, 'The email pattern is incorrect');
    this.addMessage(ErrorType.MIN_LENGTH, 'The minimum length is incorrect');
    this.addMessage(ErrorType.MAX_LENGTH, 'The maximum length is incorrect');
    this.addMessage(ErrorType.PATTERN, 'Wrong pattern');
  }
}
```

## CustomErrorMessage

```typescript
class CustomErrorMessage extends DikeErrorMessage {
  constructor();
}
```


# Interfaces


# Columns

## DikeNodeColumnDef

```typescript
interface DikeNodeColumnDef {
  column: DikeColumnDef;
  parent?: DikeNodeColumnDef;
  children?: DikeNodeColumnDef[];
}
```

## DikeColumnMoveEvent

```typescript
interface DikeColumnMoveEvent {
  type: ColumnMovement;
  movedColumns: DikeColumnDef[];
  beforeColumn?: DikeColumnDef;
  afterColumn?: DikeColumnDef;
  destinationPanel?: DikeGridPanel;
}
```


# Sorting

\<T> represents the object's shape coming from the data source.

## DikeColumnSortEvent\<T>

```typescript
interface DikeColumnSortEvent<T extends object> {
  type: 'asc' | 'desc';
  sortedColumn: DikeDataColumnDef<T, string | number | Date | boolean>;
}
```


# Row Grouping

\<T> represents the object's shape coming from the data source.

## DikeGroupRowEvent\<T>

```typescript
interface DikeGroupRowEvent<T extends object> {
  groupId: string;
  groupKey: unknown;
  groupLevel: number;

  dataColumn?: DikeDataColumnDef<T, string | number | Date | boolean>;
  dataRowEntries?: DikeGridDataRowEntry<T>[];
}
```

## DikeGroupRowCollapseEvent\<T>

```typescript
interface DikeGroupRowCollapseEvent<T extends object> extends DikeGroupRowEvent<T> {
  collapse: boolean
}
```


# Filtering

\<T> represents the object's shape coming from the data source. \<R> represents the column data type: string, number, Date, or boolean.

## Generics

<mark style="color:green;">`<T>`</mark> represents the object's shape coming from the data source.

<mark style="color:green;">`<R>`</mark> represents the column data type: <mark style="color:green;">`string`</mark>, <mark style="color:green;">`number`</mark>, <mark style="color:green;">`Date`</mark>, or <mark style="color:green;">`boolean`</mark>.

<mark style="color:green;">`<V>`</mark> represents the filter values.

## DikeFilter

```typescript
interface DikeFilter {
  logicalOperator: LogicalOperator,
  condition: string,
}
```

## DikeTextFilter

```typescript
interface DikeTextFilter extends DikeFilter {
  value?: string
}
```

## DikeTextCaseFilter

```typescript
interface DikeTextCaseFilter extends DikeTextFilter {
  caseSensitive: boolean
}
```

## DikeNumericFilter

```typescript
interface DikeNumericFilter extends DikeFilter {
  value1?: number,
  value2?: number
}
```

## DikeDateFilter

```typescript
interface DikeDateFilter extends DikeFilter {
  value1?: Date,
  value2?: Date
}
```

## DikeBinaryFilter

```typescript
interface DikeBinaryFilter extends DikeFilter {
  value?: string
}
```

## DikeFilterable\<T>

```typescript
interface DikeFilterable<T extends object> {
  columnId: string,
  dataType: DikeColumnDataType,
  fieldName: string,
  headerText: string,

  caseSensitive?: boolean,
  filter?: DikeFilter[],
  customFilterConditions?: CustomFilterConditionInstance<T>
}
```

## Condition\<T, R, V>

```typescript
interface Condition<T extends object, R extends string | number | Date | boolean, V> {
  text: string,
  value: string,
  eval: (entry: T, dataColumnDef?: DikeDataColumnDef<T, R>, values?: V) => boolean;
}
```

## DikeSelectionModel

```typescript
interface DikeSelectionModel {
  label: string,
  selected?: boolean
}
```

## DikeBinarySelectionModel

```typescript
interface DikeBinarySelectionModel extends DikeSelectionModel {
  value: string
}
```

## DikeNumericSelectionModel

```typescript
interface DikeNumericSelectionModel extends DikeSelectionModel {
  value: number
}
```

## DikeTextSelectionModel

```typescript
interface DikeTextSelectionModel extends DikeSelectionModel {
  value: string
}
```


# Editing

\<T> represents the object's shape coming from the data source.

## RowEditionActions

```typescript
interface RowEditionActions {
  edit: boolean,
  delete: boolean,
  save: boolean,
  cancel: boolean
}
```

## CrossFieldValidationSettings\<T>

```typescript
interface CrossFieldValidationSettings<T extends object> {
  customValidator?: (columnDef: DikeGridColumnDef<T>) => ValidatorFn,
  customAsyncValidator?: (columnDef: DikeGridColumnDef<T>) => AsyncValidatorFn,
  updateOn?: 'change' | 'blur' | 'submit',
  errorMessages: CustomErrorMessage
}
```

## EditionFieldSettings

```typescript
interface EditionFieldSettings {
  required?: boolean,
  validators?: ValidatorFn[],
  asyncValidators?: AsyncValidatorFn[],
  updateOn?: 'change' | 'blur' | 'submit',
  options?: DikeBinarySelectionModel[] | DikeNumericSelectionModel[] | DikeTextSelectionModel[],
  errorMessages?: CustomErrorMessage
}
```

## DikeGridEditionSettings\<T>

```typescript
interface DikeGridEditionSettings<T extends object> {
  maxRowsInEdition?: number,
  rowEditionDblClick?: boolean,
  rowEditionEnterkey?: boolean,
  rowEditionEscKey?: boolean,
  splitDeleteOperation?: boolean,
  deleteOperationRowSubsetSize?: number,
  splitUndoOperation?: boolean,
  undoOperationRowSubsetSize?: number,
  crossFieldValidation?: CrossFieldValidationSettings<T>
  waitForMultipleRowDeletions?: boolean,
  waitForMultipleRowUpdates?: boolean,
  waitForMultipleRowCancellations?: boolean,
  waitForMultipleRowRestorations?: boolean
}
```


# Injection Tokens


# Grid Structure

The following Injection Tokens apply to the Grid Structure.

## Grid Size

### GRID\_WIDTH

This Injection Token modifies the width of the DikeGrid instances.

```typescript
const GRID_WIDTH: InjectionToken<string>;
```

### GRID\_HEIGHT

This Injection Token modifies the height of the DikeGrid instances.

```typescript
const GRID_HEIGHT: InjectionToken<string>;
```

## Header

### HEADER\_ROW\_HEIGHT

This Injection Token modifies the height of the column headers.

```typescript
const HEADER_ROW_HEIGHT: InjectionToken<number>;
```

## Row Filter

### FILTER\_ROW\_HEIGHT&#x20;

This Injection Token modifies the height of the Row Filter.

```typescript
const FILTER_ROW_HEIGHT: InjectionToken<number>;
```

## Rows

### CONTENT\_ROW\_HEIGHT

This Injection Token modifies the height of every displayed row.

```typescript
const CONTENT_ROW_HEIGHT: InjectionToken<number>;
```

## Panels

### DIVISION\_PANEL\_GAP

This Injection Token modifies the default space between two adjacent panels.

```typescript
const DIVISION_PANEL_GAP: InjectionToken<number>;
```

### DIVISION\_PANEL\_LINE\_WIDTH

This Injection Token modifies the default panel-division-line width.

```typescript
const DIVISION_PANEL_LINE_WIDTH: InjectionToken<number>;
```

### DIVISION\_PANEL\_LINE\_WIDTH\_RESIZING

This Injection Token modifies the panel division line width when the panel is resizing.

```typescript
const DIVISION_PANEL_LINE_WIDTH_RESIZING: InjectionToken<number>;
```

### ROW\_GROUPING\_ROW\_HEIGHT.

This Injection Token modifies the height of the Group Panel.

```typescript
const ROW_GROUPING_ROW_HEIGHT: InjectionToken<number>;
```

### ROW\_GROUPING\_COLUMN\_HEIGHT

This Injection Token modifies the columns' height in the Group Panel.

```typescript
const ROW_GROUPING_COLUMN_HEIGHT: InjectionToken<number>;
```

### ROW\_GROUPING\_INDENT

This Injection Token shows or hides the indent per row.

```typescript
const ROW_GROUPING_INDENT: InjectionToken<boolean>;
```

### ROW\_GROUPING\_INDENT\_WIDTH&#x20;

This Injection Token modifies the width of the indent per row.

```typescript
const ROW_GROUPING_INDENT_WIDTH: InjectionToken<boolean>;
```

## Edition Toolbar

### EDITION\_TOOLBAR\_ROW\_HEIGHT.&#x20;

This Injection Token modifies the height of the Edition Toolbar Row.&#x20;

```typescript
const EDITION_TOOLBAR_ROW_HEIGHT: InjectionToken<number>;
```

### EDITION\_TOOLBAR\_ITEM\_HEIGHT&#x20;

This Injection Token modifies the height of items contained in the Edition Toolbar Row.

```typescript
const EDITION_TOOLBAR_ITEM_HEIGHT: InjectionToken<number>;
```

## Paginator

### PAGINATION\_ROW\_HEIGHT

This Injection Token modifies the height of the Pagination Row.

```typescript
const PAGINATION_ROW_HEIGHT: InjectionToken<number>;
```

## Borders

### BORDER\_WIDTH

This Injection Token modifies the border width of the primary DikeGrid container.

```typescript
const BORDER_WIDTH: InjectionToken<number>;
```

### BORDER\_GAP

This Injection Token modifies the space between the primary DikeGrid container and its external boundary.

```typescript
const BORDER_GAP: InjectionToken<number>;
```

## Column Context Menu

### COLUMN\_CONTEXT\_MENU

To suppress the context menu for all defined columns, provide this Injection Token with a false value.

```typescript
const COLUMN_CONTEXT_MENU: InjectionToken<boolean>;
```

### COLUMN\_CONTEXT\_MENU\_WIDTH

This Injection Token modifies the width of the column context menu.

```typescript
const COLUMN_CONTEXT_MENU_WIDTH: InjectionToken<number>;
```

### COLUMN\_CONTEXT\_MENU\_HEIGHT

This Injection Token modifies the height of the column context menu.

```typescript
const COLUMN_CONTEXT_MENU_HEIGHT: InjectionToken<number>;
```

## Waiting Indicator

### LOADING\_MESSAGE

This Injection Token modifies the message displayed when the DikeGrid instance is waiting for the rows of data.

```typescript
const LOADING_MESSAGE: InjectionToken<string>;
```

### PROCESSING\_MESSAGE

This Injection Token modifies the message displayed when the DikeGrid instance is waiting for an edition operation.

```typescript
const PROCESSING_MESSAGE: InjectionToken<string>;
```


# Filtering

The following Injection Tokens apply to the Filtering module.

## Debounce time

### TEXT\_FILTER\_DEBOUNCE\_TIME

This Injection Token modifies the waiting time before sending a text filter for its execution.

```typescript
const TEXT_FILTER_DEBOUNCE_TIME: InjectionToken<number>;
```

### NUMERIC\_FILTER\_DEBOUNCE\_TIME

This Injection Token modifies the waiting time before sending a numeric filter for its execution.

```typescript
const NUMERIC_FILTER_DEBOUNCE_TIME: InjectionToken<number>;
```

## Custom Filter conditions

### CUSTOM\_TEXT\_FILTER\_CONDITIONS

This Injection Token allows creating `Text` filter conditions and adding them to the existing ones.

```typescript
const CUSTOM_TEXT_FILTER_CONDITIONS: InjectionToken<CustomTextCaseFilterCondition<object>>;
```

### CUSTOM\_NUMERIC\_FILTER\_CONDITIONS

This Injection Token allows creating `Numeric` filter conditions and adding them to the existing ones.

```typescript
const CUSTOM_NUMERIC_FILTER_CONDITIONS: InjectionToken<CustomNumericFilterCondition<object>>;
```

### CUSTOM\_DATE\_FILTER\_CONDITIONS

This Injection Token allows creating `Date` filter conditions and adding them to the existing ones.

```typescript
const CUSTOM_DATE_FILTER_CONDITIONS: InjectionToken<CustomDateFilterCondition<object>>;
```

### CUSTOM\_BINARY\_FILTER\_CONDITIONS

This Injection Token allows overwriting the valid filter conditions for `Binary` types.

```typescript
const CUSTOM_BINARY_FILTER_CONDITIONS: InjectionToken<CustomBinaryFilterCondition<object>>;
```

## Custom Row Filter conditions

### CUSTOM\_ROW\_TEXT\_FILTER\_CONDITIONS

This Injection Token allows changing the default `Text` condition.

```typescript
const CUSTOM_ROW_TEXT_FILTER_CONDITIONS: InjectionToken<CustomRowTextCaseFilterCondition<object>>;
```

### CUSTOM\_ROW\_NUMERIC\_FILTER\_CONDITIONS

This Injection Token allows changing the default `Numeric` condition.

```typescript
const CUSTOM_ROW_NUMERIC_FILTER_CONDITIONS: InjectionToken<CustomRowNumericFilterCondition<object>>;
```

### CUSTOM\_ROW\_DATE\_FILTER\_CONDITIONS

This Injection Token allows changing the default `Date` condition.

```typescript
const CUSTOM_ROW_DATE_FILTER_CONDITIONS: InjectionToken<CustomRowDateFilterCondition<object>>;
```


# Editing

The following Injection Tokens apply to the Editing module.

## Edition triggers

### ROW\_EDITION\_DBLCLICK

This Injection Token disables the double-click functionality that takes a row to edition state.

```typescript
const ROW_EDITION_DBLCLICK: InjectionToken<boolean>;
```

### ROW\_EDITION\_ENTER\_KEY

This Injection Token disables the ENTER key functionality that saves a row changes.

```typescript
const ROW_EDITION_ENTER_KEY: InjectionToken<boolean>;
```

### ROW\_EDITION\_ESC\_KEY

This Injection Token disables the ESC key functionality that cancels a row edition.

```typescript
const ROW_EDITION_ESC_KEY: InjectionToken<boolean>;
```

## Validation messages

### CUSTOM\_EDITION\_ERROR\_MESSAGES

```typescript
const CUSTOM_EDITION_ERROR_MESSAGES: InjectionToken<CustomErrorMessage>;
```

## Multiple rows edition

### MAX\_ROWS\_IN\_EDITION

This Injection Token reconfigures the maximum number of rows that the DikeGrid instance can have in the edition state.

```typescript
const MAX_ROWS_IN_EDITION: InjectionToken<boolean>;
```

### WAIT\_FOR\_MULTIPLE\_ROWS\_DELETION

To avoid the DikeGrid showing the Processing Indicator during the deletion operation, provide this Injection Token with a <mark style="color:red;">`false`</mark> value.

```typescript
const WAIT_FOR_MULTIPLE_ROWS_DELETION: InjectionToken<boolean>;
```

### WAIT\_FOR\_MULTIPLE\_ROWS\_UPDATE

To avoid the DikeGrid showing the Processing Indicator during the update operation, provide this Injection Token with a <mark style="color:red;">`false`</mark> value.

```typescript
const WAIT_FOR_MULTIPLE_ROWS_UPDATE: InjectionToken<boolean>;
```

### WAIT\_FOR\_MULTIPLE\_ROWS\_CANCELATION

To avoid the DikeGrid showing the Processing Indicator during the cancelation operation, provide this Injection Token with a <mark style="color:red;">`false`</mark> value.

```typescript
const WAIT_FOR_MULTIPLE_ROWS_CANCELATION: InjectionToken<boolean>;
```

### WAIT\_FOR\_MULTIPLE\_ROWS\_RESTORATION

To avoid the DikeGrid showing the Processing Indicator during the restoration operation, provide this Injection Token with a <mark style="color:red;">`false`</mark> value.

```typescript
const WAIT_FOR_MULTIPLE_ROWS_RESTORATION: InjectionToken<boolean>;
```


# Theming

The following Injection Tokens apply to Theming

## Scrollbars

### CUSTOM\_SCROLLBARS

This Injection Token unsets the custom scrollbars.

```typescript
const CUSTOM_SCROLLBARS: InjectionToken<boolean>;
```


# Type aliases


# DataSource

\<T> represents the object's shape coming from the data source.

## DikeGridDataSourceInput\<T>

```typescript
type DikeGridDataSourceInput<T extends object> = DataSource<T> | DikeDataSource<T> | DikeGridDataSource<T> | Observable<ReadonlyArray<T> | T[]> | ReadonlyArray<T> | T[];
```

## DataSourceDecoratorFn\<T>

```typescript
type DataSourceDecoratorFn<T extends object> = (dikeDataSource: DikeDataSource<DikeGridDataRowEntry<T>>, dikeGrid: DikeGridComponent<T>) => DikeDataSource<DikeGridDataRowEntry<T>>;
```


# Columns

\<T> represents the object's shape coming from the data source. \<R> represents the column data type: string, number, Date, or boolean.

## &#x20;DikeColumnDataType

```typescript
type DikeColumnDataType = 'Text' | 'Numeric' | 'Date' | 'Binary';
```

## DisplayStatus

```typescript
type DisplayStatus = 'closed' | 'open';
```

## ColumnAlignment

```typescript
type ColumnAlignment = 'left' | 'center' | 'right' | 'start' | 'end';
```

## DikeContentPanel

```typescript
type DikeContentPanel = 'leftPanel' | 'centerPanel' | 'rightPanel';
```

## DikeGroupPanel

```typescript
type DikeGroupPanel = 'groupPanel';
```

## DikeGridPanel

```typescript
type DikeGridPanel = DikeContentPanel | DikeGroupPanel;
```

## GetterFn\<T, R>

```typescript
type GetterFn<T extends object, R extends string | number | Date | boolean> = (entry: T) => R;
```

## SetterFn\<T, R>

```typescript
type SetterFn<T extends object, R extends string | number | Date | boolean> = (entry: T, value: R) => void
```

## ColumnMovement

```typescript
type ColumnMovement = 'swap-columns' | 'join-column-groups' | 'column-after' | 'column-before' | 'columns-to-panel';
```

## CompareColumnFn

```typescript
type CompareColumnFn = (column: DikeColumnDef) => boolean;
```

## CompareNodeFn

```typescript
type CompareNodeFn = (node: DikeNodeColumnDef) => boolean;
```


# Selection

\<T> represents the object's shape coming from the data source. \<R> represents the column data type: string, number, Date, or boolean.

## SelectableFn\<T>

```typescript
type SelectableFn<T> = (entry: T) => boolean;
```


# Filtering

\<T> represents the object's shape coming from the data source. \<R> represents the column data type: string, number, Date, or boolean.

## LogicalOperator

```typescript
type LogicalOperator = 'and' | 'or';
```

## ConditionTypes

```typescript
enum ConditionType {
  EQUALS = 'equals',
  NOT_EQUALS = 'notEquals',
  CONTAINS = 'contains',
  NOT_CONTAINS = 'notContains',
  STARTS_WITH = 'startsWith',
  ENDS_WITH = 'endsWith',
  LESS_THAN = 'lessThan',
  LESS_THAN_OR_EQUAL = 'lessThanOrEqual',
  GREATER_THAN = 'greaterThan',
  GREATER_THAN_OR_EQUAL = 'greaterThanOrEqual',
  RANGE = 'range',
  EMPTY = 'empty',
  NOT_EMPTY = 'notEmpty'
}
```

## CustomFilterConditionInstance\<T>

```typescript
type CustomFilterConditionInstance<T> = 
        CustomTextCaseFilterCondition<T> | 
        CustomRowTextCaseFilterCondition<T> | 
        CustomNumericFilterCondition<T> | 
        CustomRowNumericFilterCondition<T> | 
        CustomDateFilterCondition<T> | 
        CustomRowDateFilterCondition<T> | 
        CustomBinaryFilterCondition<T> | 
        CustomMultipleNumericFilterCondition<T> | 
        CustomMultipleTextFilterCondition<T>;
```

## DikeGridCustomFilterConditions\<T>

```typescript
interface DikeGridCustomFilterConditions<T extends object> {
  customTextFilterConditions?: CustomTextCaseFilterCondition<T>;
  customNumericFilterConditions?: CustomNumericFilterCondition<T>;
  customDateFilterConditions?: CustomDateFilterCondition<T>;

  customBinaryFilterConditions?: CustomBinaryFilterCondition<T>;

  customRowTextFilterConditions?: CustomRowTextCaseFilterCondition<T>;
  customRowNumericFilterConditions?: CustomRowNumericFilterCondition<T>;
  customRowDateFilterConditions?: CustomRowDateFilterCondition<T>;
}
```


# Editing

## EditionToolbarPosition

```typescript
type EditionToolbarPosition = 'top' | 'bottom';
```

## EditionToolbarAlignment

```typescript
type EditionToolbarAlignment = 'left' | 'center' | 'right';
```

## RowStatus

```typescript
type RowStatus = 'Read' | 'Modified' | 'Deleted' | 'Editing';
```

## ErrorType

```typescript
enum ErrorType {
  MIN = 'min',
  MAX = 'max',
  REQUIRED = 'required',
  REQUIRED_TRUE = 'required',
  EMAIL = 'email',
  MIN_LENGTH = 'minlength',
  MAX_LENGTH = 'maxlength',
  PATTERN = 'pattern'
}
```


# Type Guards

\<T> represents the object's shape coming from the data source. \<R> represents the column data type: string, number, Date, or boolean.

## isDikeGroupColumnDef

```typescript
function isDikeGroupColumnDef(column: DikeColumnDef): column is DikeGroupColumnDef {
    return column instanceof DikeGroupColumnDef;
}
```

## isDikeDataColumnDef\<T, R>

```typescript
function isDikeDataColumnDef<T extends object, R extends string | number | Date | boolean>(column: DikeColumnDef): column is DikeDataColumnDef<T, R> {
    return column instanceof DikeDataColumnDef;
}
```


