External search box
In this tutorial, you will add a search input outside Handsontable that highlights matching cells as you type. You will learn how to use the Search plugin’s query() method and hot.render() to apply real-time cell highlights from an external control.
/* file: app.component.ts */import { Component, ViewChild } from '@angular/core';import { GridSettings, HotTableComponent, HotTableModule } from '@handsontable/angular-wrapper';import { RowObject } from 'handsontable/common';
const data = [ ['Alice Johnson', 'Engineering', 'Berlin', 'alice.johnson@example.com'], ['Noah Smith', 'Design', 'Warsaw', 'noah.smith@example.com'], ['Mia Garcia', 'Marketing', 'New York', 'mia.garcia@example.com'], ['Liam Brown', 'Engineering', 'Toronto', 'liam.brown@example.com'], ['Emma Davis', 'Sales', 'London', 'emma.davis@example.com'], ['Oliver Miller', 'Support', 'Madrid', 'oliver.miller@example.com'],];
@Component({ standalone: true, imports: [HotTableModule], selector: 'example1-external-search-box', template: ` <div class="example-controls-container"> <div class="controls"> <label for="external-search-input">Search rows</label> <input id="external-search-input" type="search" placeholder="Type to highlight matching cells..." style="min-width: 20rem" (input)="onSearch($event)" /> </div> </div> <hot-table [data]="data" [settings]="gridSettings"></hot-table> `,})export class AppComponent { @ViewChild(HotTableComponent, { static: false }) readonly hotTable!: HotTableComponent;
readonly data = data;
readonly gridSettings: GridSettings = { rowHeaders: true, colHeaders: ['Name', 'Team', 'Location', 'Email'], height: 'auto', width: '100%', autoWrapRow: true, autoWrapCol: true, search: true, };
private debounceTimer: ReturnType<typeof setTimeout> | undefined;
onSearch(event: Event): void { const value = (event.target as HTMLInputElement).value;
clearTimeout(this.debounceTimer); this.debounceTimer = setTimeout(() => { const hot = this.hotTable.hotInstance;
if (!hot) { return; }
hot.getPlugin('search').query(value); hot.render(); }, 120); }}/* end-file */
/* file: app.config.ts */import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';import { registerAllModules } from 'handsontable/registry';import { HOT_GLOBAL_CONFIG, HotGlobalConfig, NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
registerAllModules();
export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), { provide: HOT_GLOBAL_CONFIG, useValue: { license: NON_COMMERCIAL_LICENSE } as HotGlobalConfig, }, ],};/* end-file */<div><example1-external-search-box></example1-external-search-box></div>Enable the Search plugin
Set
search: truein grid settings:const hot = new Handsontable(container, {data,search: true,licenseKey: 'non-commercial-and-evaluation',});This enables the plugin and the default
htSearchResulthighlight class.Add an external input
Render a text input above the grid container:
const controls = document.createElement('div');const searchInput = document.createElement('input');searchInput.type = 'search';searchInput.placeholder = 'Search in table...';controls.appendChild(searchInput);container.parentElement?.insertBefore(controls, container);The input lives outside Handsontable, so you can style and place it like any other app control.
Bind the input to
query()Listen to input events, query the plugin, and re-render:
const searchPlugin = hot.getPlugin('search');searchInput.addEventListener('input', () => {searchPlugin.query(searchInput.value);hot.render();});query()updates each cell’sisSearchResultmetadata.hot.render()applies the updated highlight state.
Step 4 (optional): Debounce for large datasets
If you search very large tables, debounce the input callback to reduce render frequency:
function debounce<T extends (...args: any[]) => void>(callback: T, wait = 120) { let timeoutId: ReturnType<typeof setTimeout> | undefined;
return (...args: Parameters<T>) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => callback(...args), wait); };}Use this wrapper around your search handler when needed.
What you learned
- How to enable the
Searchplugin withsearch: truein Handsontable settings. - How to place a search input outside the grid and call
hot.getPlugin('search').query(value)on every input event. - Why you must call
hot.render()afterquery()to apply the updatedisSearchResultmetadata to cells. - How to add debouncing to limit render frequency when searching large datasets.
Next steps
- Explore highlight search matches to wrap matched text in
<mark>tags instead of using the default cell highlight class. - Add multi-column filtering to let users filter by multiple columns at once through an external panel.