# Filter types

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](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/reference/components/dkgridcolumncomponent#properties) is of type [<mark style="color:green;">`CustomFilterConditionInstance`</mark>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/reference/interfaces/filtering#diketextselectionmodel).
{% endhint %}

#### Multiple Numeric Filter

For Multiple Numeric Filter conditions, you must provide a [<mark style="color:green;">`CustomMultipleNumericFilterCondition`</mark>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/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>](https://docs.dikesoft.com/reference/classes/filtering#customtextcasefiltercondition-less-than-t-greater-than) |
| <mark style="color:blue;">`CUSTOM_NUMERIC_FILTER_CONDITIONS`</mark> | [<mark style="color:green;">`CustomNumericFilterCondition`</mark>](https://docs.dikesoft.com/reference/classes/filtering#customnumericfiltercondition-less-than-t-greater-than)   |
| <mark style="color:blue;">`CUSTOM_DATE_FILTER_CONDITIONS`</mark>    | [<mark style="color:green;">`CustomDateFilterCondition`</mark>](https://docs.dikesoft.com/reference/classes/filtering#customdatefiltercondition-less-than-t-greater-than)         |
| <mark style="color:blue;">`CUSTOM_BINARY_FILTER_CONDITIONS`</mark>  | [<mark style="color:green;">`CustomBinaryFilterCondition`</mark>](https://docs.dikesoft.com/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](https://docs.dikesoft.com/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 %}
