Saved DataGrid views
Save filters and column layouts so users can return to a familiar view.
A saved view is a snapshot of grid settings. For example, a finance team can save an “Overdue invoices” view with a date filter and amounts sorted from highest to lowest. Applying that view restores the settings to the current data.
Available in ngbootstrap 2.3.0 and later.
What is saved?
| Setting | When you apply a saved view |
|---|---|
| Sorting | Restores the current single-column sort field and direction. |
| Filters and search | Restores nested filter rules, Date values and global search text. |
| Grouping and summaries | Restores group fields, group directions and aggregate definitions. |
| Paging | Restores the page size. Always starts on page one; the previous page number is not saved. |
| Column layout | Restores order, visibility, explicit widths and sticky edges, subject to the current column configuration. |
| Locked columns | Records lock flags, but keeps the application's current locking constraints. A view cannot unlock or relock a column. |
Row data, selected rows, expanded groups/details, unfinished edits, templates, callbacks and themes are not saved.
Add saved views to a grid
Import the grid and Views control into your standalone component. Connect them with a template reference and give this grid a storage key. The default store keeps named views in memory.
import { Component } from '@angular/core';
import { ColumnDef, Datagrid, NgbDataGridViewsComponent } from '@angular-bootstrap/ngbootstrap';
interface Invoice { id: string; status: string; amount: number; }
@Component({
standalone: true,
imports: [Datagrid, NgbDataGridViewsComponent],
template: `
<ngb-datagrid-views [grid]="invoices" storageKey="invoices" />
<ngb-datagrid #invoices [columns]="columns" [data]="rows"
[dataOperations]="true" [enableSorting]="true" [filterable]="'row'" />
`,
})
export class InvoicePage {
columns: ColumnDef<Invoice>[] = [
{ field: 'id', header: 'Invoice' },
{ field: 'status', header: 'Status', filterable: true },
{ field: 'amount', header: 'Amount', type: 'number', sortable: true },
];
rows: Invoice[] = [
{ id: 'INV-2041', status: 'Overdue', amount: 4200 },
{ id: 'INV-2042', status: 'Pending', amount: 1850 },
];
}
Users choose Save as to create a named view, then select it from the View dropdown. Nothing saves automatically. To keep views after a refresh, add the local-storage adapter shown below.
The following example demonstrates
Saving, updating and restoring invoice filters and column layouts, with a choice of persistence adapters.
- Choose Overdue preset, then Save as and name it “Overdue invoices”.
- Change a filter or toggle the Customer column. The control shows Unsaved changes.
- Choose Update to save your changes, or Reset to return to the starting settings.
- Select “Overdue invoices” to apply your saved settings again.
To test a browser refresh, select This browser before saving. After refreshing, select This browser again, then choose your saved view. Loading a collection does not apply a view automatically.
| Actions | |||||
|---|---|---|---|---|---|
INV-2041 | Northwind Studio | Overdue | Aug 20, 2026 | 4200 | |
INV-2042 | Atlas Workshop | Pending | Sep 25, 2026 | 1850 | |
INV-2043 | River Labs | Overdue | Aug 28, 2026 | 7600 | |
INV-2044 | Cedar Supply | Paid | Sep 10, 2026 | 930 | |
INV-2045 | Field Works | Pending | Oct 1, 2026 | 3100 |
Local views use the demo key demo/invoice-workspace/v1. Custom persistence here is in memory, not a real network request. Switching persistence reattaches the control and captures a new Reset baseline.
Save and restore in code
You can use the snapshot API without the Views control. captureView() reads the current settings; it does not write to storage. restoreView() applies a snapshot and tells you whether it succeeded.
import { ViewChild } from '@angular/core';
import { Datagrid, NgbDataGridViewSnapshot } from '@angular-bootstrap/ngbootstrap';
// Add these members to the component that owns your grid.
@ViewChild(Datagrid) grid!: Datagrid;
saved?: NgbDataGridViewSnapshot;
message = '';
save(): void {
this.saved = this.grid.captureView();
this.message = 'Settings saved in memory.';
}
restore(): void {
if (!this.saved) return;
const result = this.grid.restoreView(this.saved);
this.message = result.success ? 'Settings restored.' : result.message;
}
Keep the snapshot after a refresh
Use the serialization helpers when writing a snapshot to browser storage or sending it to a backend. They validate the snapshot and preserve Date-valued filters. Run browser storage calls in user actions or afterNextRender, not during server rendering.
import { ngbSerializeGridView, ngbDeserializeGridView } from '@angular-bootstrap/ngbootstrap';
// In the same component, call these methods from Save / Restore buttons.
saveToBrowser(): void {
try {
localStorage.setItem('my-app/invoice-settings', ngbSerializeGridView(this.grid.captureView()));
this.message = 'Settings saved in this browser.';
} catch {
this.message = 'Could not save settings. Check browser storage permissions.';
}
}
restoreFromBrowser(): void {
try {
const json = localStorage.getItem('my-app/invoice-settings');
if (json === null) { this.message = 'No saved settings yet.'; return; }
const result = this.grid.restoreView(ngbDeserializeGridView(json));
this.message = result.success ? 'Settings restored.' : result.message;
} catch {
this.message = 'Could not read these saved settings. Your grid has not changed.';
}
}
Local and remote data
For a local array, enable [dataOperations]="true" so the grid applies the restored sort, filters and groups. For remote data, reload your rows from dataStateChange. A successful restore emits that event once, with page one and the saved page size. It does not replay separate sorting, filtering or paging events.
<!-- reloadInvoices is your application method that fetches rows for the supplied state. -->
<ngb-datagrid #invoices [columns]="columns" [data]="rows" [total]="total"
[dataOperations]="false" (dataStateChange)="reloadInvoices($event)" />
viewChange reports interactive configuration changes and restores. Use it to observe settings or implement your own explicit persistence policy. After changing inputs from application code, call captureView() when you need a fresh snapshot.
Snapshot and restoration API
| API | Behavior |
|---|---|
captureView() | Returns an independent version-1 snapshot of sorting, nested filters (including dates), search, grouping/aggregates, page size and column layout. |
restoreView(snapshot) | Returns success with ignored fields, or an invalid-snapshot/editing result with a message. Invalid data is rejected before applying anything. |
viewChange | Emits snapshots after interactive configuration changes. Capture again after application-driven input changes. |
dataStateChange | Emits once per restore, on page one. Remote applications reload here; individual sort/filter/page events are not replayed. |
NgbDataGridViewStore | Async list, save and delete methods scoped by an application-owned key. Names must be nonempty and unique, ignoring case. |
ngbSerializeGridView / ngbDeserializeGridView | Use these helpers at persistence boundaries to validate snapshots and preserve Dates. Raw JSON.stringify alone loses Date types. |
Rows, selection, expanded rows/groups, editor drafts, callbacks, templates and theme choices are excluded. Current locking, reordering and width constraints take priority. Removed fields are ignored; new columns append with configured defaults.
Choose where views are stored
| Option | Use it when | Lifetime |
|---|---|---|
| Memory (default) | You need temporary views without browser storage. | Until the store instance is discarded; not across a refresh. |
| Local storage | Users want views on the same browser between visits. | Until browser or application data is cleared. |
| Custom adapter | Views need to follow signed-in users across devices. | Defined by your backend and retention policy. |
Use local storage
import { Datagrid, NgbDataGridViewsComponent, NgbLocalStorageGridViewStore } from '@angular-bootstrap/ngbootstrap';
// In your standalone component: imports: [Datagrid, NgbDataGridViewsComponent]
readonly viewStore = new NgbLocalStorageGridViewStore();
<ngb-datagrid-views [grid]="invoices" storageKey="tenant/user/invoices" [store]="viewStore" />
<ngb-datagrid #invoices [columns]="columns" [data]="rows" />
Use a different key for each grid, user and tenant. For example, tenant-7/user-42/invoices keeps this collection separate from another user's orders grid. The adapter adds the prefix ngb:grid-views:.
Connect your backend
Implement three asynchronous methods: list views, save a view by ID, and delete a view by ID. The example below defines an application API boundary; it does not assume a particular HTTP endpoint.
Custom adapter example
import { NgbDataGridViewStore, NgbDataGridSavedView,
ngbSerializeGridView, ngbDeserializeGridView } from '@angular-bootstrap/ngbootstrap';
// Define this boundary using your authenticated backend client.
interface ViewApi {
list(key: string): Promise<{ id: string; name: string; snapshot: string }[]>;
save(key: string, view: { id: string; name: string; snapshot: string }): Promise<void>;
delete(key: string, id: string): Promise<void>;
}
class BackendViews implements NgbDataGridViewStore {
constructor(private api: ViewApi) {}
async list(key: string): Promise<NgbDataGridSavedView[]> {
return (await this.api.list(key)).map(view => ({ ...view,
snapshot: ngbDeserializeGridView(view.snapshot) }));
}
save(key: string, view: NgbDataGridSavedView): Promise<void> {
return this.api.save(key, { ...view, snapshot: ngbSerializeGridView(view.snapshot) });
}
delete(key: string, id: string): Promise<void> { return this.api.delete(key, id); }
}
The application owns authentication, authorization, retention and concurrent-write handling. Storage keys do not provide access control. Local storage is not encrypted and has no cross-tab transaction support.
Limits and restore rules
- Reset is not Undo. Reset applies the configuration captured when the Views control attached. For step-by-step configuration history, add the optional Undo/Redo directive.
- Page one is intentional. Views save page size, not the last page number.
- Save or cancel edits first. An active row, cell, new-row or external editor blocks restore and Reset.
- Current columns take priority. Removed fields are ignored. New columns append with defaults. Locked columns keep their configured position, visibility and pinning; width and reordering constraints still apply.
- Storage failures are visible. Denied storage, malformed data and failed writes show an error without replacing the grid. Correct the storage problem and retry the action; recreate the control to retry loading its collection.
- Keep the connection stable. Recreate the Views control when changing its grid, store, user or tenant key. A new control captures a new Reset baseline.
Snapshots accept plain JSON-compatible values and valid Dates. Unsupported versions, functions, cycles, nonfinite numbers and custom class instances are rejected before a view is applied. Row data is not serialized.
Keyboard, screen readers and server rendering
Tab through native labeled controls. Use arrow keys in the view selector, Enter to submit a name, and Escape to cancel. Save/error messages are announced; closing a naming or confirmation action returns focus to the selector.
Grid, Pager and Splitter render the initial configuration on the server. Browser measurements and stored views load after rendering. This demo renders all three without a client-only placeholder.
Grid rows retain row semantics alongside drag-and-drop. Complete spreadsheet arrow-key navigation, cell ranges and keyboard column resizing are not part of this release. Evaluate custom templates, dense layouts, sticky overlays and theme contrast with your screen reader.
Full DataGrid API · Theme variables and customization