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?

SettingWhen you apply a saved view
SortingRestores the current single-column sort field and direction.
Filters and searchRestores nested filter rules, Date values and global search text.
Grouping and summariesRestores group fields, group directions and aggregate definitions.
PagingRestores the page size. Always starts on page one; the previous page number is not saved.
Column layoutRestores order, visibility, explicit widths and sticky edges, subject to the current column configuration.
Locked columnsRecords 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.

  1. Choose Overdue preset, then Save as and name it “Overdue invoices”.
  2. Change a filter or toggle the Customer column. The control shows Unsaved changes.
  3. Choose Update to save your changes, or Reset to return to the starting settings.
  4. 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.

Drag column headers here to group rows

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

APIBehavior
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.
viewChangeEmits snapshots after interactive configuration changes. Capture again after application-driven input changes.
dataStateChangeEmits once per restore, on page one. Remote applications reload here; individual sort/filter/page events are not replayed.
NgbDataGridViewStoreAsync list, save and delete methods scoped by an application-owned key. Names must be nonempty and unique, ignoring case.
ngbSerializeGridView / ngbDeserializeGridViewUse 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

OptionUse it whenLifetime
Memory (default)You need temporary views without browser storage.Until the store instance is discarded; not across a refresh.
Local storageUsers want views on the same browser between visits.Until browser or application data is cleared.
Custom adapterViews 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

This project is not affiliated with ng-bootstrap or ngx-bootstrap. Those projects focus mainly on Bootstrap components for Angular. ngbootstrap focuses on Angular UI for data-heavy apps, especially Data Grid, Angular-native Form Builder, drag and drop workflows, documentation examples, and performance-focused Angular patterns.