---
title: "Filter API"
framework: angular
version: "36.1.0"
---

# Filter API

You can access and set the models for filters through the grid API, or access individual filter instances directly for more control. This page details how to do both.

> **Note**
>
> The filter model can be saved and restored as part of [Grid State](https://www.ag-grid.com/angular-data-grid/grid-state/).

## Get / Set All Filter Models

It is possible to get the state of all filters using the grid API method `getFilterModel()`, and to set the state using `setFilterModel()`. These methods manage the filters states via the `getModel()` and `setModel()` methods of the individual filters.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getFilterModel` | `Function` |  |  | Gets the current state of all the column filters. Used for saving filter state. Modules (any of): [`TextFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`NumberFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`DateFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`SetFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`MultiFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`CustomFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `setFilterModel` | `Function` |  |  | Sets the state of all the column filters. Provide it with what you get from `getFilterModel()` to restore filter state. If inferring cell data types, and row data is initially empty or yet to be set, the filter model will be applied asynchronously after row data is added. To always perform this synchronously, set `cellDataType = false` on the default column definition, or provide cell data types for every column. Modules (any of): [`TextFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`NumberFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`DateFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`SetFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`MultiFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`CustomFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

```ts
// Gets filter model via the grid API
const model = this.gridApi.getFilterModel();

// Sets the filter model via the grid API
this.gridApi.setFilterModel(model);
```

The filter model represents the state of filters for all columns and has the following structure:

```js
// Sample filter model via getFilterModel()
{
    athlete: {
        filterType: 'text',
        type: 'startsWith',
        filter: 'mich'
    },
    age: {
        filterType: 'number',
        type: 'lessThan',
        filter: 30
    }
}
```

This is useful if you want to save the global filter state and apply it at a later stage. It is also useful for server-side filtering, where you want to pass the filter state to the server.

### Reset All Filters

You can reset all filters by doing the following:

```ts
this.gridApi.setFilterModel(null);
```

### Example: Get / Set All Filter Models

The example below shows getting and setting all the filter models in action.

- `Save Filter Model` saves the current filter state, which will then be displayed.
- `Restore Saved Filter Model` restores the saved filter state back into the grid.
- `Set Custom Filter Model` takes a custom hard-coded filter model and applies it to the grid.
- `Reset Filters` will clear all active filters.
- `Destroy Filter` destroys the filter for the **Athlete** column by calling `gridApi.destroyFilter('athlete')`. This removes any active filter from that column, and will cause the filter to be created with new initialisation values the next time it is interacted with.

(Note: the example uses the Enterprise-only [Set Filter](https://www.ag-grid.com/angular-data-grid/filter-set/)).

#### Filter Model

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  DateFilterModule,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IDateFilterParams,
  ModuleRegistry,
  NumberFilterModule,
  SideBarDef,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  SetFilterModule,
} from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
  TextFilterModule,
  NumberFilterModule,
  DateFilterModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div>
      <div class="button-group">
        <button (click)="saveFilterModel()">Save Filter Model</button>
        <button (click)="restoreFilterModel()">
          Restore Saved Filter Model
        </button>
        <button
          (click)="restoreFromHardCoded()"
          title="Name = 'Mich%', Country = ['Ireland', 'United States'], Age < 30, Date < 01/01/2010"
        >
          Set Custom Filter Model
        </button>
        <button (click)="clearFilters()">Reset Filters</button>
        <button (click)="destroyFilter()">Destroy Filter</button>
      </div>
    </div>
    <div>
      <div class="button-group">
        Saved Filters: <span id="savedFilters">(none)</span>
      </div>
    </div>

    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [sideBar]="sideBar"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [
    { field: "athlete", filter: "agTextColumnFilter" },
    { field: "age", filter: "agNumberColumnFilter", maxWidth: 100 },
    { field: "country" },
    { field: "year", maxWidth: 100 },
    {
      field: "date",
      filter: "agDateColumnFilter",
      filterParams: filterParams,
    },
    { field: "sport" },
    { field: "gold", filter: "agNumberColumnFilter" },
    { field: "silver", filter: "agNumberColumnFilter" },
    { field: "bronze", filter: "agNumberColumnFilter" },
    { field: "total", filter: "agNumberColumnFilter" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
    filter: true,
  };
  sideBar: SideBarDef | string | string[] | boolean | null = "filters";
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  clearFilters() {
    this.gridApi.setFilterModel(null);
  }

  saveFilterModel() {
    savedFilterModel = this.gridApi.getFilterModel();
    const keys = Object.keys(savedFilterModel);
    const savedFilters: string = keys.length > 0 ? keys.join(", ") : "(none)";
    (document.querySelector("#savedFilters") as any).textContent = savedFilters;
  }

  restoreFilterModel() {
    this.gridApi.setFilterModel(savedFilterModel);
  }

  restoreFromHardCoded() {
    const hardcodedFilter = {
      country: {
        type: "set",
        values: ["Ireland", "United States"],
      },
      age: { type: "lessThan", filter: "30" },
      athlete: { type: "startsWith", filter: "Mich" },
      date: { type: "lessThan", dateFrom: "2010-01-01" },
    };
    this.gridApi.setFilterModel(hardcodedFilter);
  }

  destroyFilter() {
    this.gridApi.destroyFilter("athlete");
  }

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.gridApi = params.api;

    params.api.getToolPanelInstance("filters")!.expandFilters();

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}

const filterParams: IDateFilterParams = {
  comparator: (filterLocalDateAtMidnight: Date, cellValue: string) => {
    const dateAsString = cellValue;
    if (dateAsString == null) return -1;
    const dateParts = dateAsString.split("/");
    const cellDate = new Date(
      Number(dateParts[2]),
      Number(dateParts[1]) - 1,
      Number(dateParts[0]),
    );
    if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
      return 0;
    }
    if (cellDate < filterLocalDateAtMidnight) {
      return -1;
    }
    if (cellDate > filterLocalDateAtMidnight) {
      return 1;
    }
    return 0;
  },
};
let savedFilterModel: any = null;
```

[Live example: Filter Model](https://www.ag-grid.com/examples/filter-api/filter-model/angular)

## Get / Set Individual Filter Model

It is also possible to get or set the filter model for a specific filter, including your own custom filters.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getColumnFilterModel` | `Function` |  |  | Gets the current filter model for the specified column. Will return `null` if no active filter. Modules (any of): [`TextFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`NumberFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`DateFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`SetFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`MultiFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`CustomFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `setColumnFilterModel` | `Function` |  |  | Sets the filter model for the specified column. Setting a `model` of `null` will reset the filter (make inactive). Must wait on the response before calling `api.onFilterChanged()`. Modules (any of): [`TextFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`NumberFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`DateFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`SetFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`MultiFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`CustomFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

### Re-running Grid Filtering

After filters have been changed via their API, you must ensure the method `gridApi.onFilterChanged()` is called to tell the grid to filter the rows again. If `gridApi.onFilterChanged()` is not called, the grid will still show the data relevant to the filters before they were updated through the API.

```js
// Set a filter model
await api.setColumnFilterModel('name', {
    filterType: 'text',
    type: 'startsWith',
    filter: 'abc',
});

// Tell grid to run filter operation again
api.onFilterChanged();
```

### Reset Individual Filters

You can reset a filter to its original state by setting the model to `null`.

```js
// Set the model to null
await api.setColumnFilterModel('name', null);

// Tell grid to run filter operation again
api.onFilterChanged();
```

### Example: Get / Set Individual Filter Model

The example below shows getting and setting an individual filter model in action.

- `Save Filter Model` saves the **Athlete** filter state, which will then be displayed.
- `Restore Saved Filter Model` restores the saved **Athlete** filter state back into the grid.
- `Set Custom Filter Model` takes a custom hard-coded **Athlete** filter model and applies it to the grid.
- `Reset Filter` will clear the **Athlete** filter.

#### Individual Filter Model

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ICombinedSimpleModel,
  IDateFilterParams,
  ModuleRegistry,
  NumberFilterModule,
  SideBarDef,
  TextFilterModel,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  SetFilterModule,
} from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
  TextFilterModule,
  NumberFilterModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div>
      <div class="button-group">
        <button (click)="saveFilterModel()">Save Filter Model</button>
        <button (click)="restoreFilterModel()">
          Restore Saved Filter Model
        </button>
        <button
          (click)="restoreFromHardCoded()"
          title="Name = 'Mich%', Country = ['Ireland', 'United States'], Age < 30, Date < 01/01/2010"
        >
          Set Custom Filter Model
        </button>
        <button (click)="clearFilter()">Reset Filter</button>
      </div>
    </div>
    <div>
      <div class="button-group">
        Saved Filters: <span id="savedFilters">(none)</span>
      </div>
    </div>

    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [sideBar]="sideBar"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [
    { field: "athlete", filter: "agTextColumnFilter" },
    { field: "age", filter: "agNumberColumnFilter", maxWidth: 100 },
    { field: "country", filter: "agTextColumnFilter" },
    { field: "year", filter: "agNumberColumnFilter", maxWidth: 100 },
    { field: "sport", filter: "agTextColumnFilter" },
    { field: "gold", filter: "agNumberColumnFilter" },
    { field: "silver", filter: "agNumberColumnFilter" },
    { field: "bronze", filter: "agNumberColumnFilter" },
    { field: "total", filter: "agNumberColumnFilter" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
    filter: true,
  };
  sideBar: SideBarDef | string | string[] | boolean | null = "filters";
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  clearFilter() {
    this.gridApi.setColumnFilterModel("athlete", null).then(() => {
      this.gridApi.onFilterChanged();
    });
  }

  saveFilterModel() {
    savedFilterModel = this.gridApi.getColumnFilterModel("athlete");
    const convertTextFilterModel = (model: TextFilterModel) => {
      return `${(model as TextFilterModel).type} ${(model as TextFilterModel).filter}`;
    };
    const convertCombinedFilterModel = (
      model: ICombinedSimpleModel<TextFilterModel>,
    ) => {
      return model
        .conditions!.map((condition) => convertTextFilterModel(condition))
        .join(` ${model.operator} `);
    };
    let savedFilterString: string;
    if (!savedFilterModel) {
      savedFilterString = "(none)";
    } else if (
      (savedFilterModel as ICombinedSimpleModel<TextFilterModel>).operator
    ) {
      savedFilterString = convertCombinedFilterModel(
        savedFilterModel as ICombinedSimpleModel<TextFilterModel>,
      );
    } else {
      savedFilterString = convertTextFilterModel(
        savedFilterModel as TextFilterModel,
      );
    }
    (document.querySelector("#savedFilters") as any).innerText =
      savedFilterString;
  }

  restoreFilterModel() {
    this.gridApi.setColumnFilterModel("athlete", savedFilterModel).then(() => {
      this.gridApi.onFilterChanged();
    });
  }

  restoreFromHardCoded() {
    const hardcodedFilter = { type: "startsWith", filter: "Mich" };
    this.gridApi.setColumnFilterModel("athlete", hardcodedFilter).then(() => {
      this.gridApi.onFilterChanged();
    });
  }

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.gridApi = params.api;

    params.api.getToolPanelInstance("filters")!.expandFilters(["athlete"]);

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}

const filterParams: IDateFilterParams = {
  comparator: (filterLocalDateAtMidnight: Date, cellValue: string) => {
    const dateAsString = cellValue;
    if (dateAsString == null) return -1;
    const dateParts = dateAsString.split("/");
    const cellDate = new Date(
      Number(dateParts[2]),
      Number(dateParts[1]) - 1,
      Number(dateParts[0]),
    );
    if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
      return 0;
    }
    if (cellDate < filterLocalDateAtMidnight) {
      return -1;
    }
    if (cellDate > filterLocalDateAtMidnight) {
      return 1;
    }
    return 0;
  },
};
let savedFilterModel:
  | TextFilterModel
  | ICombinedSimpleModel<TextFilterModel>
  | null = null;
```

[Live example: Individual Filter Model](https://www.ag-grid.com/examples/filter-api/filter-model-individual/angular)

## Accessing Individual Filters

It certain cases, it may be needed to interact directly with a specific filter. For instance, [Refreshing Values](https://www.ag-grid.com/angular-data-grid/filter-set-filter-list/#refreshing-values) on the Set Filter.

Grid-provided filters are split into two parts - the filter UI component and the filter handler (which performs the filter logic).

When `enableFilterHandlers = true`, [Custom Filter Components](https://www.ag-grid.com/angular-data-grid/component-filter/) are also split into two parts.

Note that the [Multi Filter](https://www.ag-grid.com/angular-data-grid/filter-multi/) will only have a filter handler when `enableFilterHandlers = true`.

To access the filter UI component, use `api.getColumnFilterInstance(colKey)`.

To access the filter handler, use `api.getColumnFilterHandler(colKey)`.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getColumnFilterInstance` | `Function` |  |  | Returns the filter component instance for a column. For getting/setting models for individual column filters, use `getColumnFilterModel` and `setColumnFilterModel` instead of this. `key` can be a column ID or a `Column` object. Modules (any of): [`TextFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`NumberFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`DateFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`SetFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`MultiFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`CustomFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `getColumnFilterHandler` | `Function` |  |  | Returns the filter handler instance for a column. Used when `enableFilterHandlers = true`, or when using a grid-provided filter. If using a `SimpleColumnFilter`, this will be an object containing the provided `doesFilterPass` callback. `key` can be a column ID or a `Column` object. Modules (any of): [`TextFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`NumberFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`DateFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`SetFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`MultiFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/), [`CustomFilterModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

```js
// Get a reference to the 'name' filter UI instance
const filterInstance = await api.getColumnFilterInstance('name');
```

If using a custom filter, any other methods you have added will also be present, allowing bespoke behaviour to be added to your filter.

### Example: Accessing Individual Filters

The example below shows how you can interact with an individual filter instance, using the Set Filter as an example.

- `Get Mini Filter Text` will print the text from the Set Filter's Mini Filter to the console.
- `Save Mini Filter Text` will save the Mini Filter text.
- `Restore Mini Filter Text` will restore the Mini Filter text from the saved state.

(Note: the example uses the Enterprise-only [Set Filter](https://www.ag-grid.com/angular-data-grid/filter-set/)).

#### Accessing Individual Filters

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  SetFilterUi,
  SideBarDef,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  SetFilterModule,
} from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  FiltersToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  SetFilterModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <button (click)="getMiniFilterText()">Get Mini Filter Text</button>
      <button (click)="saveMiniFilterText()">Save Mini Filter Text</button>
      <button (click)="restoreMiniFilterText()">
        Restore Mini Filter Text
      </button>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [sideBar]="sideBar"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [{ field: "athlete", filter: "agSetColumnFilter" }];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
    filter: true,
  };
  sideBar: SideBarDef | string | string[] | boolean | null = "filters";
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  getMiniFilterText() {
    this.gridApi
      .getColumnFilterInstance<SetFilterUi>("athlete")
      .then((athleteFilter) => {
        console.log(athleteFilter!.getMiniFilter());
      });
  }

  saveMiniFilterText() {
    this.gridApi
      .getColumnFilterInstance<SetFilterUi>("athlete")
      .then((athleteFilter) => {
        savedMiniFilterText = athleteFilter!.getMiniFilter();
      });
  }

  restoreMiniFilterText() {
    this.gridApi
      .getColumnFilterInstance<SetFilterUi>("athlete")
      .then((athleteFilter) => {
        athleteFilter!.setMiniFilter(savedMiniFilterText);
      });
  }

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.gridApi = params.api;

    params.api.getToolPanelInstance("filters")!.expandFilters();

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}

let savedMiniFilterText: string | null = "";
```

[Live example: Accessing Individual Filters](https://www.ag-grid.com/examples/filter-api/filter-api/angular)

## Read-only Filter UI

Sometimes it maybe useful to strictly control the filters used by the grid via API, whilst still exposing filter settings in-use to users. The `readOnly` filter parameter changes the behaviour of all provided column filters so their UI is read-only. In this mode, API filter changes are still honoured and reflected in the UI:

```ts
<ag-grid-angular
    [columnDefs]="columnDefs"
    /* other grid options ... */ />

this.columnDefs = [
    {
        field: 'age',
        filter: true,
        filterParams: {
            readOnly: true
        }
    }
];
```

The following example demonstrates all of the Provided Filters with `readOnly: true` enabled:

- Simple Filters have a read-only display with no buttons; if there is no 2nd condition set then the join operator and 2nd condition are hidden:
  - `athlete` column demonstrates [Text Filter](https://www.ag-grid.com/angular-data-grid/filter-text/).
  - `age` and `year` columns demonstrate [Number Filter](https://www.ag-grid.com/angular-data-grid/filter-number/).
  - `date` column demonstrates [Date Filter](https://www.ag-grid.com/angular-data-grid/filter-date/).
- [Set Filter](https://www.ag-grid.com/angular-data-grid/filter-set/) allows Mini Filter searching of values, but value inclusion/exclusion cannot be toggled; buttons are also hidden, and pressing enter in the Mini Filter input has no effect:
  - `country`, `gold`, `silver` and `bronze` columns demonstrate [Set Filter](https://www.ag-grid.com/angular-data-grid/filter-set/).
- [Multi Filter](https://www.ag-grid.com/angular-data-grid/filter-multi/) has no direct behaviour change, sub-filters need to be individually made read-only. `readOnly: true` is needed to affect any associated [Floating Filters](https://www.ag-grid.com/angular-data-grid/floating-filters/).
  - `sport` column demonstrates [Multi Filter](https://www.ag-grid.com/angular-data-grid/filter-multi/).
- [Floating Filters](https://www.ag-grid.com/angular-data-grid/floating-filters/) are enabled and inherit `readOnly: true` from their parent, disabling any UI input.
- Buttons above the grid provide API interactions to configure the filters.
- `Print Country` button prints the country model to the developer console.

#### Read-only Filter UI

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  DateFilterModule,
  FilterWrapperParams,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IMultiFilterParams,
  ISetFilterParams,
  ITextFilterParams,
  ModuleRegistry,
  NumberFilterModule,
  SetFilterHandler,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  MultiFilterModule,
  SetFilterModule,
} from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  MultiFilterModule,
  SetFilterModule,
  TextFilterModule,
  NumberFilterModule,
  DateFilterModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <span class="button-group">
        <button (click)="irelandAndUk()">Ireland &amp; UK</button>
        <button (click)="endingStan()">Countries Ending 'stan'</button>
        <button (click)="printCountryModel()">Print Country</button>
        <button (click)="clearCountryFilter()">Clear Country</button>
        <button (click)="destroyCountryFilter()">Destroy Country</button>
      </span>
      <span class="button-group">
        <button (click)="ageBelow25()">Age Below 25</button>
        <button (click)="ageAbove30()">Age Above 30</button>
        <button (click)="ageBelow25OrAbove30()">
          Age Below 25 or Above 30
        </button>
        <button (click)="ageBetween25And30()">Age Between 25 and 30</button>
        <button (click)="clearAgeFilter()">Clear Age Filter</button>
      </span>
      <span class="button-group">
        <button (click)="after2010()">Date after 01/01/2010</button>
        <button (click)="before2012()">Date before 01/01/2012</button>
        <button (click)="dateCombined()">Date combined</button>
        <button (click)="clearDateFilter()">Clear Date Filter</button>
      </span>
      <span class="button-group">
        <button (click)="sportStartsWithS()">Sport starts with S</button>
        <button (click)="sportEndsWithG()">Sport ends with G</button>
        <button (click)="sportsCombined()">
          Sport starts with S and ends with G
        </button>
        <button (click)="clearSportFilter()">Clear Sport Filter</button>
      </span>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [suppressSetFilterByDefault]="true"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [
    {
      field: "athlete",
    },
    {
      field: "age",
    },
    {
      field: "country",
      filter: "agSetColumnFilter",
    },
    {
      field: "year",
      maxWidth: 120,
    },
    {
      field: "date",
      minWidth: 215,
      suppressHeaderMenuButton: true,
    },
    {
      field: "sport",
      suppressHeaderMenuButton: true,
      filter: "agMultiColumnFilter",
      filterParams: {
        filters: [
          {
            filter: "agTextColumnFilter",
            filterParams: { readOnly: true } as ITextFilterParams,
          },
          {
            filter: "agSetColumnFilter",
            filterParams: { readOnly: true } as ISetFilterParams,
          },
        ],
        readOnly: true,
      } as IMultiFilterParams,
    },
    {
      field: "gold",
      filter: "agSetColumnFilter",
    },
    {
      field: "silver",
      filter: "agSetColumnFilter",
    },
    {
      field: "bronze",
      filter: "agSetColumnFilter",
    },
    { field: "total", filter: false },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
    filter: true,
    floatingFilter: true,
    filterParams: defaultFilterParams,
  };
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  irelandAndUk() {
    this.gridApi
      .setColumnFilterModel("country", { values: ["Ireland", "Great Britain"] })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  clearCountryFilter() {
    this.gridApi.setColumnFilterModel("country", null).then(() => {
      this.gridApi.onFilterChanged();
    });
  }

  destroyCountryFilter() {
    this.gridApi.destroyFilter("country");
  }

  endingStan() {
    const countriesEndingWithStan = this.gridApi
      .getColumnFilterHandler<SetFilterHandler>("country")!
      .getFilterKeys()
      .filter(function (value: any) {
        return value.indexOf("stan") === value.length - 4;
      });
    this.gridApi
      .setColumnFilterModel("country", { values: countriesEndingWithStan })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  printCountryModel() {
    const model = this.gridApi.getColumnFilterModel("country");
    if (model) {
      console.log("Country model is: " + JSON.stringify(model));
    } else {
      console.log("Country model filter is not active");
    }
  }

  sportStartsWithS() {
    this.gridApi
      .setColumnFilterModel("sport", {
        filterModels: [
          {
            type: "startsWith",
            filter: "s",
          },
        ],
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  sportEndsWithG() {
    this.gridApi
      .setColumnFilterModel("sport", {
        filterModels: [
          {
            type: "endsWith",
            filter: "g",
          },
        ],
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  sportsCombined() {
    this.gridApi
      .setColumnFilterModel("sport", {
        filterModels: [
          {
            conditions: [
              {
                type: "endsWith",
                filter: "g",
              },
              {
                type: "startsWith",
                filter: "s",
              },
            ],
            operator: "AND",
          },
        ],
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  ageBelow25() {
    this.gridApi
      .setColumnFilterModel("age", {
        type: "lessThan",
        filter: 25,
        filterTo: null,
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  ageAbove30() {
    this.gridApi
      .setColumnFilterModel("age", {
        type: "greaterThan",
        filter: 30,
        filterTo: null,
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  ageBelow25OrAbove30() {
    this.gridApi
      .setColumnFilterModel("age", {
        conditions: [
          {
            type: "greaterThan",
            filter: 30,
            filterTo: null,
          },
          {
            type: "lessThan",
            filter: 25,
            filterTo: null,
          },
        ],
        operator: "OR",
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  ageBetween25And30() {
    this.gridApi
      .setColumnFilterModel("age", {
        type: "inRange",
        filter: 25,
        filterTo: 30,
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  clearAgeFilter() {
    this.gridApi.setColumnFilterModel("age", null).then(() => {
      this.gridApi.onFilterChanged();
    });
  }

  after2010() {
    this.gridApi
      .setColumnFilterModel("date", {
        type: "greaterThan",
        dateFrom: "2010-01-01",
        dateTo: null,
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  before2012() {
    this.gridApi
      .setColumnFilterModel("date", {
        type: "lessThan",
        dateFrom: "2012-01-01",
        dateTo: null,
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  dateCombined() {
    this.gridApi
      .setColumnFilterModel("date", {
        conditions: [
          {
            type: "lessThan",
            dateFrom: "2012-01-01",
            dateTo: null,
          },
          {
            type: "greaterThan",
            dateFrom: "2010-01-01",
            dateTo: null,
          },
        ],
        operator: "OR",
      })
      .then(() => {
        this.gridApi.onFilterChanged();
      });
  }

  clearDateFilter() {
    this.gridApi.setColumnFilterModel("date", null).then(() => {
      this.gridApi.onFilterChanged();
    });
  }

  clearSportFilter() {
    this.gridApi.setColumnFilterModel("sport", null).then(() => {
      this.gridApi.onFilterChanged();
    });
  }

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.gridApi = params.api;

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe(
        (data) =>
          (this.rowData = data.map((rowData) => {
            const dateParts = rowData.date.split("/");
            return {
              ...rowData,
              date: `${dateParts[2]}-${dateParts[1]}-${dateParts[0]}`,
            };
          })),
      );
  }
}

const defaultFilterParams: FilterWrapperParams = { readOnly: true };
```

[Live example: Read-only Filter UI](https://www.ag-grid.com/examples/filter-api/filter-api-readonly/angular)

## Launching Filters

How filters are launched can be customised (unless grid option `columnMenu = 'legacy'`).

`colDef.suppressHeaderFilterButton = true` can be used to disable the button in the header that opens the filter.

The filter can also be launched via `api.showColumnFilter(columnKey)` and hidden via `api.hideColumnFilter()`.

The following example demonstrates launching the filter:

- The **Athlete** column has a filter button in the header to launch the filter.
- The **Age** column has a floating filter, so the header button is automatically hidden.
- The **Country** column has the filter button hidden via `colDef.suppressHeaderFilterButton`. The filter can still be opened via the API by clicking the `Open Country Filter` button.
- The **Year** column has a floating filter and the header button is also suppressed, so has a slightly different display style when the filter is active.

#### Launching Filters

```ts
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberFilterModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);
import { IOlympicData } from "./interfaces";

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div>
      <div class="button-group">
        <button (click)="openCountryFilter()">Open Country Filter</button>
      </div>
    </div>

    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [rowData]="rowData"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<IOlympicData>;

  columnDefs: ColDef[] = [
    { field: "athlete" },
    { field: "age", floatingFilter: true },
    { field: "country", suppressHeaderFilterButton: true },
    {
      field: "year",
      maxWidth: 120,
      floatingFilter: true,
      suppressHeaderFilterButton: true,
    },
    { field: "sport" },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total", filter: false },
  ];
  defaultColDef: ColDef = {
    flex: 1,
    minWidth: 150,
    filter: true,
  };
  rowData!: IOlympicData[];

  constructor(private http: HttpClient) {}

  openCountryFilter() {
    this.gridApi.showColumnFilter("country");
  }

  onGridReady(params: GridReadyEvent<IOlympicData>) {
    this.gridApi = params.api;

    this.http
      .get<
        IOlympicData[]
      >("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Launching Filters](https://www.ag-grid.com/examples/filter-api/launching-filters/angular)

## Filter Events

Filtering causes the following events to be emitted:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `filterOpened` | `FilterOpenedEvent` |  |  | Filter has been opened. |
| `filterChanged` | `FilterChangedEvent` |  |  | Filter has been modified and applied. |
| `filterModified` | `FilterModifiedEvent` |  |  | Filter was modified but not applied (when using `enableFilterHandlers = false`). Used when filters have 'Apply' buttons. |
| `filterUiChanged` | `FilterUiChangedEvent` |  |  | Filter UI was modified (when using `enableFilterHandlers = true`). |
| `floatingFilterUiChanged` | `FloatingFilterUiChangedEvent` |  |  | Floating filter UI modified (when using `enableFilterHandlers = true`). |
