---
product: "AG Grid"
title: "Advanced Filter - Custom Filter Options"
description: "Custom Filter Options defined for a column are also offered in the Advanced Filter, so an expression can use the same options as the column filter."
enterprise: true
framework: vue
version: "36.2.0"
related:
    - title: "Columns & Filter Options"
      url: "https://www.ag-grid.com/vue-data-grid/filter-advanced-columns/"
    - title: "Input & Builder"
      url: "https://www.ag-grid.com/vue-data-grid/filter-advanced-input-builder/"
    - title: "Filter Model / API"
      url: "https://www.ag-grid.com/vue-data-grid/filter-advanced-api/"
llms: "https://www.ag-grid.com/llms.txt"
---

# Advanced Filter - Custom Filter Options

Custom Filter Options defined for a column are also offered in the Advanced Filter, so an expression can use the same options as the column filter.

## Configuring Custom Filter Options

The Advanced Filter accepts [Custom Filter Options](https://www.ag-grid.com/vue-data-grid/filter-conditions/#custom-filter-options) the same way as Column Filters. Each Custom Filter Option is an `IFilterOptionDef` with the following properties.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `displayKey` | `string` |  |  |  |
| `displayName` | `string` |  |  |  |
| `predicate` | `Function` |  |  |  |
| `numberOfInputs` | `0 \| 1 \| 2` |  |  |  |

Options taking two values are validated like `is between`: the first value must be less than the second, or equal to it where `inRangeInclusive = true`.

The `predicate` only runs with the Client-Side Row Model. With the Server-Side Row Model the option is sent to the server as its `displayKey` in the filter model, as for the [Set Filter options](https://www.ag-grid.com/vue-data-grid/filter-advanced-columns/#set-filters).

The following example demonstrates custom filter options taking different numbers of values:

- The **Athlete** column has `Starts With A` (no values) and `Does Not Start With` (one value).
- The **Age** column has `Even Numbers` (no values) and `Between (Exclusive)` (two values).
- The **Date** column has `Leap Year` (no values) and `Between (Exclusive)` (two dates).

#### Custom Filter Options

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  DateFilterModule,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IDateFilterParams,
  INumberFilterParams,
  ITextFilterParams,
  ModuleRegistry,
  NumberFilterModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  AdvancedFilterModule,
  ColumnMenuModule,
  ContextMenuModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";

if (process.env.NODE_ENV !== "production") {
  // Enable extended validations only for development
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextFilterModule,
  NumberFilterModule,
  DateFilterModule,
  AdvancedFilterModule,
  ClientSideRowModelModule,
  ColumnMenuModule,
  ContextMenuModule,
]);

const athleteFilterParams: ITextFilterParams = {
  filterOptions: [
    "contains",
    {
      displayKey: "startsWithA",
      displayName: "Starts With A",
      numberOfInputs: 0,
      predicate: (_, cellValue) =>
        cellValue != null && cellValue.startsWith("A"),
    },
    {
      displayKey: "notStartsWith",
      displayName: "Does Not Start With",
      numberOfInputs: 1,
      predicate: ([filterValue], cellValue) =>
        cellValue != null &&
        !cellValue.toLowerCase().startsWith(String(filterValue).toLowerCase()),
    },
  ],
};

const ageFilterParams: INumberFilterParams = {
  filterOptions: [
    "equals",
    {
      displayKey: "evenNumbers",
      displayName: "Even Numbers",
      numberOfInputs: 0,
      predicate: (_, cellValue) => cellValue != null && cellValue % 2 === 0,
    },
    {
      displayKey: "betweenExclusive",
      displayName: "Between (Exclusive)",
      numberOfInputs: 2,
      predicate: ([from, to], cellValue) =>
        cellValue != null && cellValue > from && cellValue < to,
    },
  ],
};

const dateFilterParams: IDateFilterParams = {
  filterOptions: [
    "equals",
    {
      displayKey: "leapYear",
      displayName: "Leap Year",
      numberOfInputs: 0,
      predicate: (_, cellValue) => {
        if (cellValue == null) {
          return false;
        }
        const year = Number(cellValue.split("-")[0]);
        return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
      },
    },
    {
      displayKey: "betweenExclusive",
      displayName: "Between (Exclusive)",
      numberOfInputs: 2,
      predicate: ([from, to], cellValue) => {
        if (cellValue == null) {
          return false;
        }
        // Built as a local date: the filter's own values are local midnight, and
        // `new Date('YYYY-MM-DD')` would be UTC midnight, so the two would not line up.
        const [year, month, day] = cellValue.split("-").map(Number);
        const cellDate = new Date(year, month - 1, day);
        return cellDate > from && cellDate < to;
      },
    },
  ],
};

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :enableAdvancedFilter="true"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<ColDef[]>([
      { field: "athlete", filterParams: athleteFilterParams },
      { field: "age", minWidth: 120, filterParams: ageFilterParams },
      {
        field: "date",
        filter: "agDateColumnFilter",
        filterParams: dateFilterParams,
      },
      { field: "sport" },
      { field: "gold" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
      minWidth: 180,
      filter: true,
    });
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) =>
        params.api!.setGridOption(
          "rowData",
          // The supplied dates are `dd/mm/yyyy` strings, which is a text column. Convert them to
          // `yyyy-mm-dd` so the column is a Date (String) one and its options filter on dates.
          data.map((rowData) => {
            const [day, month, year] = rowData.date.split("/");
            return {
              ...rowData,
              date: `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`,
            };
          }),
        );

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Custom Filter Options](https://www.ag-grid.com/examples/filter-advanced-custom-filter-options/custom-filter-options/vue3/)

```ts
<ag-grid-vue
    :columnDefs="columnDefs"
    /* other grid options ... */>
</ag-grid-vue>

this.columnDefs = [
    {
        field: 'age',
        filterParams: {
            filterOptions: [
                'equals',
                {
                    displayKey: 'evenNumbers',
                    displayName: 'Even Numbers',
                    numberOfInputs: 0,
                    predicate: (_values, cellValue) => cellValue != null && cellValue % 2 === 0,
                },
                {
                    displayKey: 'betweenExclusive',
                    displayName: 'Between (Exclusive)',
                    numberOfInputs: 2,
                    predicate: ([from, to], cellValue) => cellValue != null && cellValue > from && cellValue < to,
                },
            ],
        },
    },
];
```

## Using Custom Filter Options in the Advanced Filter Input

Custom Filter Options are typed into the Advanced Filter input using their `displayName`, followed by its values. Using the options from the example above these are some example inputs:

```shell
[Athlete] Starts With A
[Athlete] Does Not Start With "Michael"
[Age] Even Numbers
[Age] Between (Exclusive) (30, 40)
[Date] Between (Exclusive) ("2008-08-20", "2008-08-25")
```

Values are quoted according to the column's Cell Data Type, as for the built-in options: numbers are unquoted, everything else is quoted. Two values are separated by a comma; the surrounding brackets are optional. Where the `displayKey` has a [localised](https://www.ag-grid.com/vue-data-grid/localisation/) entry, that text is used as the option name instead.

A `displayKey` is resolved against the column being filtered, so different columns can reuse the same key. Reusing an Option Key from the [table of standard options](https://www.ag-grid.com/vue-data-grid/filter-advanced-columns/#filter-parameters), for example `contains`, replaces that built-in option for the column, the same as it does in the column filter.

## Filter Model

A condition using a Custom Filter Option stores the `displayKey` in `type`, and the values in `filter` and `filterTo`. See [Filter Model / API](https://www.ag-grid.com/vue-data-grid/filter-advanced-api/) for saving and restoring the Advanced Filter state.

```js
const advancedFilterModel = {
    filterType: 'number',
    colId: 'age',
    type: 'betweenExclusive',
    filter: 30,
    filterTo: 40,
};
```
