---
title: "Find"
enterprise: true
framework: angular
version: "36.1.0"
---

# Find

Find allows for values to be searched within the grid, with all matches highlighted and navigable, similar to find (`^ Ctrl` + `F`) within the browser.

> **Note**
>
> Find is only compatible with the [Client-Side Row Model](https://www.ag-grid.com/angular-data-grid/row-models/).

## Enabling Find

### Using Quick Access Toolbar

The recommended way to display the Find input is as a [Quick Access Toolbar](https://www.ag-grid.com/angular-data-grid/toolbar/) item. This keeps the Find UI integrated with the grid and requires no additional markup.

#### Find with Toolbar

```ts
import { HttpClient } from "@angular/common/http";
import { Component } from "@angular/core";

import { AgGridAngular } from "ag-grid-angular";
import {
  ClientSideRowModelModule,
  ColDef,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { FindModule, ToolbarModule } from "ag-grid-enterprise";

import "./styles.css";

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

ModuleRegistry.registerModules([
  FindModule,
  ToolbarModule,
  ClientSideRowModelModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [rowData]="rowData"
    [toolbar]="toolbar"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "athlete" },
    { field: "country" },
    { field: "sport" },
    { field: "year" },
    { field: "age", minWidth: 100 },
    { field: "gold", minWidth: 100 },
    { field: "silver", minWidth: 100 },
    { field: "bronze", minWidth: 100 },
  ];
  rowData!: any[];

  toolbar = {
    items: ["agFindToolbarItem" as const],
  };

  constructor(private http: HttpClient) {}

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

[Live example: Find with Toolbar](https://www.ag-grid.com/examples/find/find-toolbar/angular)

The configuration used in the example above is:

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

this.toolbar = {
    items: ['agFindToolbarItem'],
};
```

### Using Grid Options

Find can also be enabled directly via the grid option `findSearchValue`, passing the text to search for.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `findSearchValue` | `string` |  |  | Text to find within the grid. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

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

this.findSearchValue = 'rowing';
```

The grid API methods `findNext()`, `findPrevious()`, and `findGoTo(matchNumber)` can be used to move between the matches, or `findClearActive()` can be used to clear the active match.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `findNext` | `Function` |  |  | Go to the next match. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `findPrevious` | `Function` |  |  | Go to the previous match. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `findGoTo` | `Function` |  |  | Go to the provided match (first match is `1`). By default, if the provided match is already active, this will do nothing. If `force` is set to `true`, this will instead reset the active match to that provided (e.g. scroll the grid). Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `findClearActive` | `Function` |  |  | Clear the active match. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

Changing the Find search value, changing the active match, or updates to the grid that cause changes to the visible cells (e.g. changing columns/rows) trigger the `findChanged` event. The event contains details on the active match, as well as the total number of matches.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `findChanged` | `FindChangedEvent` |  |  | Find details have changed (e.g. Find search value, active match, or updates to grid cells). |

The active match and the total number of matches can also be retrieved via the API.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `findGetTotalMatches` | `Function` |  |  | Get the total number of matches. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `findGetActiveMatch` | `Function` |  |  | Get the active match, or `undefined` if no active match. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

#### Find

```ts
import { HttpClient } from "@angular/common/http";
import { Component, ElementRef, ViewChild } from "@angular/core";

import { AgGridAngular } from "ag-grid-angular";
import {
  ClientSideRowModelModule,
  ColDef,
  FindChangedEvent,
  GridApi,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { FindModule } from "ag-grid-enterprise";

import "./styles.css";

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

ModuleRegistry.registerModules([FindModule, ClientSideRowModelModule]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <div class="example-controls">
        <span>Find:</span>
        <input
          type="text"
          (input)="onInput($event)"
          (keydown)="onKeyDown($event)"
        />
        <button (click)="previous()">Previous</button>
        <button (click)="next()">Next</button>
        <span>{{ activeMatchNum }}</span>
      </div>
      <div class="example-controls">
        <span>Go to match:</span>
        <input #goToInput type="number" />
        <button (click)="goToFind()">Go To</button>
      </div>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [columnDefs]="columnDefs"
      [rowData]="rowData"
      [findSearchValue]="findSearchValue"
      (findChanged)="onFindChanged($event)"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  @ViewChild("goToInput", { read: ElementRef }) public goToInput!: ElementRef;

  private gridApi!: GridApi;

  columnDefs: ColDef[] = [
    { field: "athlete" },
    { field: "country" },
    { field: "sport" },
    { field: "year" },
    { field: "age", minWidth: 100 },
    { field: "gold", minWidth: 100 },
    { field: "silver", minWidth: 100 },
    { field: "bronze", minWidth: 100 },
  ];
  rowData!: any[];

  activeMatchNum: string = "";

  findSearchValue: string | undefined;

  constructor(private http: HttpClient) {}

  onFindChanged(event: FindChangedEvent) {
    const { activeMatch, totalMatches, findSearchValue } = event;
    this.activeMatchNum = findSearchValue?.length
      ? `${activeMatch?.numOverall ?? 0}/${totalMatches}`
      : "";
    console.log("findChanged", event);
  }

  next() {
    this.gridApi.findNext();
  }

  previous() {
    this.gridApi.findPrevious();
  }

  goToFind() {
    const num = Number(
      (this.goToInput.nativeElement as HTMLInputElement).value,
    );
    if (isNaN(num) || num < 0) {
      return;
    }
    this.gridApi.findGoTo(num);
  }

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

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

  onInput(event: Event): void {
    this.findSearchValue = (event.target as HTMLInputElement).value;
  }

  onKeyDown(event: KeyboardEvent): void {
    if (event.key === "Enter") {
      event.preventDefault();
      const backwards = event.shiftKey;
      if (backwards) {
        this.previous();
      } else {
        this.next();
      }
    }
  }
}
```

[Live example: Find](https://www.ag-grid.com/examples/find/find/angular)

## Using Find with Cell Components

By default, Find searches within the [Formatted Value](https://www.ag-grid.com/angular-data-grid/value-formatters/) of the cell, or the raw cell value if there is no Value Formatter. This is what is displayed in the cell by default.

[Cell Components](https://www.ag-grid.com/angular-data-grid/component-cell-renderer/) may display text that does not appear in the cell value. To enable Find to search within this additional text, the `getFindText` callback can be implemented on the Column Definition. Find will search within this value for matches.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getFindText` | `GetFindTextFunc` |  |  | When using Find with custom cell renderers, this allows providing a custom value to search within. E.g. if the cell renderer is displaying text that is different from the cell formatted value. Returning `null` means Find will not search within the cell. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

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

this.columnDefs = [
    {
        field: 'year',
        getFindText: params => `Year is ${params.value}`,
    }
];
```

When providing a custom cell component, the component is responsible for highlighting any matches and active matches within the cell. The grid API provides the following methods to help with this.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `findGetNumMatches` | `Function` |  |  | Get the number of matches within the provided cell. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |
| `findGetParts` | `Function` |  |  | Get the parts of a cell value, including matches and active match. Used for custom cell components. Module: [`FindModule`](https://www.ag-grid.com/angular-data-grid/modules/). |

The following example demonstrates a custom cell component in the `Year` column that implements match highlighting using the methods above. The find input is provided by the toolbar. The custom cell component reuses the grid CSS classes `ag-find-match` and `ag-find-active-match` to apply the same styling as the default grid cell component.

#### Find with Cell Components

```ts
import { HttpClient } from "@angular/common/http";
import { Component } from "@angular/core";

import { AgGridAngular } from "ag-grid-angular";
import {
  ClientSideRowModelModule,
  ColDef,
  FirstDataRenderedEvent,
  GetFindTextParams,
  GridReadyEvent,
  ModuleRegistry,
  enableDevValidations,
} from "ag-grid-community";
import { FindModule, ToolbarModule } from "ag-grid-enterprise";

import { FindRenderer } from "./find-renderer.component";
import "./styles.css";

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

ModuleRegistry.registerModules([
  FindModule,
  ToolbarModule,
  ClientSideRowModelModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular, FindRenderer],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [rowData]="rowData"
    [findSearchValue]="findSearchValue"
    [toolbar]="toolbar"
    (gridReady)="onGridReady($event)"
    (firstDataRendered)="onFirstDataRendered($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "athlete" },
    { field: "country" },
    {
      field: "year",
      cellRenderer: FindRenderer,
      getFindText: (params: GetFindTextParams) => {
        const cellValue =
          params.getValueFormatted() ?? params.value?.toString();
        if (!cellValue?.length) {
          return null;
        }
        return `Year is ${cellValue}`;
      },
    },
  ];
  rowData!: any[];

  findSearchValue: string = "e";

  toolbar = {
    items: ["agFindToolbarItem" as const],
  };

  constructor(private http: HttpClient) {}

  onFirstDataRendered(event: FirstDataRenderedEvent) {
    event.api.findNext();
  }

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

[Live example: Find with Cell Components](https://www.ag-grid.com/examples/find/find-cell-components/angular)

> **Note**
>
> Find does not work with the [Animate Show Changed Cell Component](https://www.ag-grid.com/angular-data-grid/change-cell-renderers/#animate-show-changed-cells) or the [Animate Slide Cell Component](https://www.ag-grid.com/angular-data-grid/change-cell-renderers/#animate-slide-cells). If using these, provide a `getFindText` that returns `null` to exclude them from the search results. The same approach should also be used if manually specifying `agCheckboxCellRenderer`.

## Customising Find

Find can be customised by providing an object of type `FindOptions` to the grid option `findOptions`.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `currentPageOnly` | `boolean` |  |  | Match values in the current page only (when pagination enabled). |
| `caseSensitive` | `boolean` |  |  | Match case of values. |
| `searchDetail` | `boolean` |  |  | Perform searches across Detail Grids or Custom Detail Cells when using Master/Detail. |

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

this.findOptions = {
    caseSensitive: true,
    currentPageOnly: true,
};
```

The following example demonstrates performing a case sensitive search, and finding matches within the current page only:

#### Customising Find

```ts
import { HttpClient } from "@angular/common/http";
import { Component, ElementRef, ViewChild } from "@angular/core";

import { AgGridAngular } from "ag-grid-angular";
import {
  ClientSideRowModelModule,
  ColDef,
  FindChangedEvent,
  FindOptions,
  GridApi,
  GridReadyEvent,
  ModuleRegistry,
  PaginationModule,
  PinnedRowModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  FindModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  ToolbarModule,
} from "ag-grid-enterprise";

import "./styles.css";

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

ModuleRegistry.registerModules([
  FindModule,
  ToolbarModule,
  RowGroupingModule,
  RowGroupingPanelModule,
  PinnedRowModule,
  ClientSideRowModelModule,
  PaginationModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="example-wrapper">
    <div class="example-header">
      <div class="example-controls">
        <label>
          <span>caseSensitive:</span>
          <input
            id="caseSensitive"
            type="checkbox"
            (change)="toggleCaseSensitive($event)"
            checked=""
          />
        </label>
        <label>
          <span>currentPageOnly:</span>
          <input
            id="currentPageOnly"
            type="checkbox"
            (change)="toggleCurrentPageOnly($event)"
            checked=""
          />
        </label>
      </div>
      <div class="example-controls">
        <span>Go to match:</span>
        <input #goToInput type="number" />
        <button (click)="goToFind()">Go To</button>
      </div>
      <div>{{ activeMatch }}</div>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      [pinnedTopRowData]="pinnedTopRowData"
      [pinnedBottomRowData]="pinnedBottomRowData"
      [columnDefs]="columnDefs"
      [defaultColDef]="defaultColDef"
      [pagination]="true"
      [paginationPageSize]="paginationPageSize"
      [paginationPageSizeSelector]="paginationPageSizeSelector"
      [toolbar]="toolbar"
      [findOptions]="findOptions"
      [rowData]="rowData"
      (findChanged)="onFindChanged($event)"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  @ViewChild("goToInput", { read: ElementRef }) public goToInput!: ElementRef;

  private gridApi!: GridApi;

  pinnedTopRowData: any[] = [{ athlete: "Top" }];
  pinnedBottomRowData: any[] = [{ athlete: "Bottom" }];
  columnDefs: ColDef[] = [
    { field: "athlete" },
    { field: "country" },
    { field: "sport", rowGroup: true, hide: true },
    { field: "year" },
    { field: "age", minWidth: 100 },
    { field: "gold", minWidth: 100 },
    { field: "silver", minWidth: 100 },
    { field: "bronze", minWidth: 100 },
  ];
  defaultColDef: ColDef = {
    enableRowGroup: true,
  };
  paginationPageSize = 5;
  paginationPageSizeSelector: number[] | boolean = [5, 10];
  rowData!: any[];

  activeMatch: string = "";

  toolbar = {
    items: [
      "agRowGroupPanelToolbarItem" as const,
      "agFindToolbarItem" as const,
    ],
  };
  findOptions: FindOptions = {
    caseSensitive: true,
    currentPageOnly: true,
  };

  constructor(private http: HttpClient) {}

  onFindChanged(event: FindChangedEvent) {
    const { activeMatch } = event;
    this.activeMatch = activeMatch
      ? `Active match: { pinned: ${activeMatch.node.rowPinned}, row index: ${activeMatch.node.rowIndex}, column: ${activeMatch.column?.getColId()}, match number in cell: ${activeMatch.numInMatch} }`
      : "";
  }

  goToFind() {
    const num = Number(
      (this.goToInput.nativeElement as HTMLInputElement).value,
    );
    if (isNaN(num) || num < 0) {
      return;
    }
    this.gridApi.findGoTo(num);
  }

  toggleCaseSensitive(event: Event) {
    const caseSensitive = (event.target as HTMLInputElement).checked;
    this.findOptions = {
      ...this.findOptions,
      caseSensitive,
    };
  }

  toggleCurrentPageOnly(event: Event) {
    const currentPageOnly = (event.target as HTMLInputElement).checked;
    this.findOptions = {
      ...this.findOptions,
      currentPageOnly,
    };
  }

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

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

[Live example: Customising Find](https://www.ag-grid.com/examples/find/customising-find/angular)

## Find with Master / Detail

When using [Master / Detail](https://www.ag-grid.com/angular-data-grid/master-detail/), Find will not search within detail rows by default (either [Detail Grids](https://www.ag-grid.com/angular-data-grid/master-detail-grids/) or [Custom Details](https://www.ag-grid.com/angular-data-grid/master-detail-custom-detail/)). To enable Find to search within detail rows, set `searchDetail` within `findOptions` to `true`:

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

this.findOptions = {
    searchDetail: true,
};
```

### Detail Grids

If a row containing a Detail Grid is expanded, Find will automatically search within the Detail Grid. If the master row is not expanded, the grid does not exist yet, so Find does not know how many matches there are. Find cannot create all of the Detail Grids as there may be a very large number of them.

If you want Find to search within collapsed detail rows, then you must provide the `getFindMatches` callback to the `detailCellRendererParams` grid option. This tells Find how many matches are expected to be in the Detail Grid. If the active match moves to within the Detail Grid, the Detail Grid will automatically be expanded.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getFindMatches` | `GetFindMatches` |  |  | If using Find across Master / Detail and the Detail Grid is not open, this will be called to work out the number of matches that would be within the Detail Grid. |

The following example demonstrates Find across nested Master / Detail Grids:

#### Find with Detail Grids

```ts
import { Component } from "@angular/core";

import { AgGridAngular } from "ag-grid-angular";
import {
  ClientSideRowModelModule,
  ColDef,
  FindOptions,
  FirstDataRenderedEvent,
  GetDetailRowDataParams,
  GetFindMatchesParams,
  GetRowIdParams,
  IDetailCellRendererParams,
  ModuleRegistry,
  RowApiModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  FindModule,
  MasterDetailModule,
  ToolbarModule,
} from "ag-grid-enterprise";

import { getData } from "./data";
import "./styles.css";

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

ModuleRegistry.registerModules([
  FindModule,
  ToolbarModule,
  ClientSideRowModelModule,
  MasterDetailModule,
  RowApiModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [rowData]="rowData"
    [masterDetail]="true"
    [getRowId]="getRowId"
    [detailCellRendererParams]="detailCellRendererParams"
    [findOptions]="findOptions"
    [toolbar]="toolbar"
    (firstDataRendered)="onFirstDataRendered($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "a1", cellRenderer: "agGroupCellRenderer" },
    { field: "b1" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
  };
  rowData = getData();
  getRowId = (params: GetRowIdParams) => params.data.a1;
  findOptions: FindOptions = {
    searchDetail: true,
  };
  toolbar = {
    items: ["agFindToolbarItem" as const],
  };
  detailCellRendererParams: Partial<IDetailCellRendererParams> = {
    // level 2 grid options
    detailGridOptions: {
      columnDefs: [
        { field: "a2", cellRenderer: "agGroupCellRenderer" },
        { field: "b2" },
      ],
      defaultColDef: {
        flex: 1,
      },
      masterDetail: true,
      detailRowHeight: 240,
      getRowId: (params: GetRowIdParams) => params.data.a2,
      findOptions: {
        searchDetail: true,
      },
      detailCellRendererParams: {
        // level 3 grid options
        detailGridOptions: {
          columnDefs: [
            { field: "a3", cellRenderer: "agGroupCellRenderer" },
            { field: "b3" },
          ],
          defaultColDef: {
            flex: 1,
          },
          getRowId: (params: GetRowIdParams) => params.data.a3,
        },
        getDetailRowData: (params: GetDetailRowDataParams) => {
          params.successCallback(params.data.children);
        },
        getFindMatches: (params: GetFindMatchesParams) =>
          this.getFindMatches(params),
      } as IDetailCellRendererParams,
    },
    getDetailRowData: (params: GetDetailRowDataParams) => {
      params.successCallback(params.data.children);
    },
    getFindMatches: (params: GetFindMatchesParams) =>
      this.getFindMatches(params),
  };

  onFirstDataRendered(event: FirstDataRenderedEvent) {
    event.api.getDisplayedRowAtIndex(0)?.setExpanded(true);
  }

  private getFindMatches(params: GetFindMatchesParams) {
    const getMatchesForValue = params.getMatchesForValue;
    let numMatches = 0;
    const checkRow = (row: any) => {
      for (const key of Object.keys(row)) {
        if (key === "children") {
          row.children.forEach((child: any) => checkRow(child));
        } else {
          numMatches += getMatchesForValue(row[key]);
        }
      }
    };
    params.data.children.forEach(checkRow);
    return numMatches;
  }
}
```

[Live example: Find with Detail Grids](https://www.ag-grid.com/examples/find/find-detail-grid/angular)

### Custom Detail

For Find to work with Custom Detail Cells, Find needs to know how many matches are within the detail row. This is done by providing the `getFindMatches` callback to the `detailCellRendererParams` grid option. This tells Find how many matches are expected to be in the Custom Detail. If the active match moves to within the Custom Detail, the Custom Detail will automatically be expanded.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getFindMatches` | `GetFindMatches` |  |  | If using Find across Master / Detail, this will be called to work out the number of matches that would be within the custom detail cell. |

The Custom Detail Cell Component is responsible for highlighting matches, similar to [Custom Cell Components](#using-find-with-cell-components).

The following example demonstrates Find across Custom Details:

#### Find with Custom Details

```ts
import { HttpClient } from "@angular/common/http";
import { Component } from "@angular/core";

import { AgGridAngular } from "ag-grid-angular";
import {
  ClientSideRowModelModule,
  ColDef,
  FindDetailCellRendererParams,
  FindOptions,
  FirstDataRenderedEvent,
  GetFindMatchesParams,
  GridReadyEvent,
  ModuleRegistry,
  RowApiModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  FindModule,
  MasterDetailModule,
  ToolbarModule,
} from "ag-grid-enterprise";

import { DetailCellRenderer } from "./detail-cell-renderer.component";
import "./styles.css";

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

ModuleRegistry.registerModules([
  FindModule,
  ToolbarModule,
  ClientSideRowModelModule,
  MasterDetailModule,
  RowApiModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [rowData]="rowData"
    [masterDetail]="true"
    [detailCellRenderer]="detailCellRenderer"
    [detailCellRendererParams]="detailCellRendererParams"
    [detailRowHeight]="100"
    [findOptions]="findOptions"
    [toolbar]="toolbar"
    (firstDataRendered)="onFirstDataRendered($event)"
    (gridReady)="onGridReady($event)"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    // group cell renderer needed for expand / collapse icons
    { field: "name", cellRenderer: "agGroupCellRenderer" },
    { field: "account" },
    { field: "calls" },
  ];
  rowData!: any[];

  detailCellRenderer = DetailCellRenderer;

  detailCellRendererParams: FindDetailCellRendererParams = {
    getFindMatches: (params: GetFindMatchesParams) => {
      return params.getMatchesForValue("My Custom Detail");
    },
  };

  toolbar = {
    items: ["agFindToolbarItem" as const],
  };

  findOptions: FindOptions = {
    searchDetail: true,
  };

  constructor(private http: HttpClient) {}

  onFirstDataRendered(event: FirstDataRenderedEvent) {
    event.api.getDisplayedRowAtIndex(0)?.setExpanded(true);
  }

  onGridReady(params: GridReadyEvent) {
    this.http
      .get<
        any[]
      >("https://www.ag-grid.com/example-assets/master-detail-data.json")
      .subscribe((data) => (this.rowData = data));
  }
}
```

[Live example: Find with Custom Details](https://www.ag-grid.com/examples/find/find-custom-detail/angular)

## Find with Full Width Rows

For Find to work with [Full Width Rows](https://www.ag-grid.com/angular-data-grid/full-width-rows/), Find needs to know how many matches are within the row. This is done by providing the `getFindMatches` callback to the `fullWidthCellRendererParams` grid option. This tells Find how many matches are expected to be in the Full Width Row.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getFindMatches` | `GetFindMatches` |  |  | If using Find with full width rows, this will be called to work out the number of matches that would be within the full width row. |

The Full Width Row Component is responsible for highlighting matches, similar to [Custom Cell Components](#using-find-with-cell-components).

The following example demonstrates Find with Full Width Rows:

#### Find with Full Width Rows

```ts
import { Component } from "@angular/core";

import { AgGridAngular } from "ag-grid-angular";
import {
  ClientSideRowModelModule,
  ColDef,
  FindFullWidthCellRendererParams,
  GetFindMatchesParams,
  IsFullWidthRowParams,
  ModuleRegistry,
  RowHeightParams,
  enableDevValidations,
} from "ag-grid-community";
import { FindModule, ToolbarModule } from "ag-grid-enterprise";

import { getData, getLatinText } from "./data";
import { FullWidthCellRenderer } from "./full-width-cell-renderer.component";
import "./styles.css";

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

ModuleRegistry.registerModules([
  FindModule,
  ToolbarModule,
  ClientSideRowModelModule,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [rowData]="rowData"
    [getRowHeight]="getRowHeight"
    [isFullWidthRow]="isFullWidthRow"
    [fullWidthCellRenderer]="fullWidthCellRenderer"
    [fullWidthCellRendererParams]="fullWidthCellRendererParams"
    [toolbar]="toolbar"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    { field: "name" },
    { field: "continent" },
    { field: "language" },
  ];
  defaultColDef: ColDef = {
    flex: 1,
  };
  rowData = getData();
  getRowHeight = (params: RowHeightParams) => {
    // return 100px height for full width rows
    if (this.isFullWidth(params.data)) {
      return 100;
    }
  };
  isFullWidthRow = (params: IsFullWidthRowParams) => {
    return this.isFullWidth(params.rowNode.data);
  };
  fullWidthCellRenderer = FullWidthCellRenderer;
  fullWidthCellRendererParams: FindFullWidthCellRendererParams = {
    getFindMatches: (params: GetFindMatchesParams) => {
      const getMatchesForValue = params.getMatchesForValue;
      // this example only implements searching across part of the renderer
      let numMatches = getMatchesForValue("Sample Text in a Paragraph");
      getLatinText().forEach((paragraph) => {
        numMatches += getMatchesForValue(paragraph);
      });
      return numMatches;
    },
  };

  toolbar = {
    items: ["agFindToolbarItem" as const],
  };

  private isFullWidth(data: any) {
    // return true when country is Peru, France or Italy
    return ["Peru", "France", "Italy"].indexOf(data.name) >= 0;
  }
}
```

[Live example: Find with Full Width Rows](https://www.ag-grid.com/examples/find/find-full-width/angular)

## Find with Custom Group Row Component

Using Find with a [Custom Group Row Inner Component](https://www.ag-grid.com/angular-data-grid/grouping-group-rows/#custom-inner-renderer) (`groupRowRendererParams.innerRenderer`) or a [Custom Group Row Component](https://www.ag-grid.com/angular-data-grid/grouping-group-rows/#custom-cell-renderer) (`groupRowRenderer`) is similar to using [Using Find with Cell Components](#using-find-with-cell-components). If the component displays text that does not appear in the cell value, the `getFindText` callback can be implemented on the `groupRowRendererParams` grid option. Find will search within this value for matches.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getFindText` | `GetFindTextFunc` |  |  | When using Find with a custom group row renderer, this allows providing a custom value to search within. E.g. if the group row renderer is displaying text that is different from the formatted value. Returning `null` means Find will not search within the group row. |

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

this.groupRowRendererParams = {
    getFindText: params => `Group value is ${params.value}`,
};
```

## Content to Search

Find is designed to search within "visible" cell contents.

Searching is not performed within hidden columns. Columns not displayed due to [Column Groups](https://www.ag-grid.com/angular-data-grid/column-groups/) being expanded/collapsed are counted as being hidden.

The rows are searched after filtering and sorting have been performed.

Searching will be performed within the children of [Collapsed Row Groups](https://www.ag-grid.com/angular-data-grid/grouping-opening-groups/). When the active match is set to a child row within a collapsed group, the group is expanded (along with its parents if necessary).

When using [Row Pagination](https://www.ag-grid.com/angular-data-grid/row-pagination/), searching will be performed across all pages by default.

See the [Customising Find](#customising-find) section for an example of using Find with Row Grouping, as well as how to customise behaviour for Pagination.

If data is mutated outside of the grid (e.g. not via grid options or API methods), Find will not re-run automatically. This would apply to situations where `api.refreshCells()` or `api.redrawRows()` are being used. To get Find to update, `api.findRefresh()` should be called after either of these API methods.
