How to Fix AngularJS ui-grid Filter Not Working with External Text Input Field: Troubleshooting Guide
AngularJS ui-grid (formerly ng-grid) is a powerful data grid library for AngularJS applications, offering features like sorting, pagination, and filtering out of the box. While ui-grid’s built-in column filters work seamlessly, many developers encounter issues when integrating external text input fields (e.g., a search box above the grid) to filter grid data. Common problems include the filter not updating as the user types, partial results, or no results at all.
This guide will walk you through diagnosing and fixing issues with external filters in ui-grid. We’ll cover common causes, step-by-step troubleshooting, advanced scenarios, and best practices to ensure your external filter works reliably.
An "external filter" refers to a text input field (e.g., <input type="text">) placed outside the ui-grid that dynamically filters grid rows as the user types. The expected behavior is:
User types in the input field.
The grid immediately updates to show only rows matching the input text.
When this fails, common symptoms include:
No grid updates when typing.
Only partial rows are filtered.
Errors in the browser console (critical for diagnosis!).
Check the browser console (F12 in Chrome/Firefox) for errors/warnings. ui-grid and AngularJS often log issues here (e.g., gridApi is undefined, ng-model not found).
Ensure ui-grid is properly loaded: Confirm ui-grid.js and ui-grid.css are included in your HTML.
Verify the grid renders with data (no filter applied). If the grid is empty, fix data binding first.
The filter function determines which rows to show. It takes grid rows and the filter value, returning true (show row) or false (hide row).
Example Filter Function:
// Controller code function filterGridRows(row, filterText) { if (!filterText) return true; // Show all rows if filter is empty const lowerFilter = filterText.toLowerCase(); // Check if row matches filter (adjust properties to match your data!) return row.entity.name.toLowerCase().includes(lowerFilter) || row.entity.email.toLowerCase().includes(lowerFilter); }
Troubleshooting:
Test the function in isolation with sample data:
// Test with a sample row const testRow = { entity: { name: "John Doe", email: "[email protected]" } }; console.log(filterGridRows(testRow, "john")); // Should return true console.log(filterGridRows(testRow, "jane")); // Should return false
Common mistakes:
Using === instead of includes() for partial matches.
Forgetting to normalize case (e.g., "John" vs "john").
Referencing non-existent properties (e.g., row.name instead of row.entity.name—ui-grid wraps data in entity).
ui-grid doesn’t auto-refresh when external data changes. You must explicitly notify it via gridApi.
Example: Watch Filter and Refresh Grid
// In your controller, after defining gridOptions vm.gridOptions.onRegisterApi = function(gridApi) { vm.gridApi = gridApi; // Store gridApi for later use // Watch for filter changes and refresh $scope.$watch('vm.externalFilter', function(newVal) { if (!vm.gridApi) return; // Exit if gridApi isn't ready // Apply filter to grid rows const filteredRows = vm.gridApi.core.getVisibleRows().filter(row => filterGridRows(row, newVal) ); // Notify ui-grid to update visibility vm.gridApi.core.setRowVisibleBatch(filteredRows.map(row => row.uid), true); // Alternatively, refresh the entire grid (simpler but less efficient) vm.gridApi.core.notifyDataChange(vm.gridApi.grid.CONSTANTS.CHANGE.ALL); }); };
Key Notes:
getVisibleRows() returns all rows (use getRenderedRows() for pagination-aware filtering).
notifyDataChange(CHANGE.ALL) triggers a full refresh (use for simplicity; CHANGE.FILTER for performance).