Save Persistence Example
This example demonstrates how to implement save and restore functionality for the diagram in ngDiagram using Angular features.
import '@angular/compiler';import { Component, inject, Injector } from '@angular/core';import { initializeModel, NgDiagramBackgroundComponent, NgDiagramComponent, NgDiagramModelService, provideNgDiagram, type Model, type NgDiagramConfig,} from 'ng-diagram';import { NavBarComponent } from './nav-bar/nav-bar.component';import { SaveStateService } from './save.service';
@Component({ imports: [NgDiagramComponent, NgDiagramBackgroundComponent, NavBarComponent], template: ` <nav-bar (loadModel)="loadModel($event)"></nav-bar> <div class="not-content diagram"> <ng-diagram [model]="model" [config]="config"> <ng-diagram-background /> </ng-diagram> </div> `, styleUrl: './diagram.component.scss', providers: [NgDiagramModelService, SaveStateService, provideNgDiagram()],})export class DiagramComponent { private injector = inject(Injector); config = { zoom: { max: 3, zoomToFit: { onInit: true, padding: 155, }, }, } satisfies NgDiagramConfig;
model = initializeModel({ nodes: [ { id: '1', position: { x: 0, y: 0 }, data: {}, }, { id: '2', position: { x: 100, y: 50 }, data: {}, }, { id: '3', position: { x: 200, y: 100 }, data: {}, }, ], });
loadModel(model: Partial<Model>): void { this.model = initializeModel(model, this.injector); }}3 collapsed lines
import { Component, computed, inject, output, signal } from '@angular/core';import { NgDiagramModelService, type Model } from 'ng-diagram';import { SaveStateService } from '../save.service';
@Component({ selector: 'nav-bar', template: ` <div class="nav-bar"> <div class="status">{{ statusMessage() }}</div> <button (click)="save()">Save</button> <button (click)="load()" [disabled]="!isSaved()">Load</button> <button class="clear" (click)="clear()" [disabled]="!isSaved()"> Clear </button> </div> `, styleUrl: './nav-bar.component.scss',})export class NavBarComponent {44 collapsed lines
loadModel = output<Partial<Model>>();
private readonly saveStateService = inject(SaveStateService); private readonly modelService = inject(NgDiagramModelService); isSaved = computed(() => { return this.saveStateService.state() !== null; });
private statusTimeout: ReturnType<typeof setTimeout> | null = null; protected statusMessage = signal('');
save(): void { const model = this.modelService.toJSON(); this.saveStateService.save(model); this.showStatus('State has been successfully saved!'); }
load(): void { const model = this.saveStateService.load();
if (model) { this.loadModel.emit(model); this.showStatus('State has been loaded!'); } }
clear(): void { if (window.confirm('Are you sure you want to clear the saved state?')) { this.saveStateService.clear(); this.showStatus('State has been cleared!'); } }
showStatus(msg: string): void { this.statusMessage.set(msg);
if (this.statusTimeout) { clearTimeout(this.statusTimeout); } this.statusTimeout = setTimeout(() => { this.statusMessage.set(''); this.statusTimeout = null; }, 6000); }}8 collapsed lines
import { effect, Injectable, signal } from '@angular/core';import { type Edge, type Metadata, type Node } from 'ng-diagram';
@Injectable()export class SaveStateService { private KEY = 'ngDiagramSaveStateKey';
state = signal<string | null>(localStorage.getItem(this.KEY)); stateSync = effect(() => { if (this.state() === null) { localStorage.removeItem(this.KEY); } else { localStorage.setItem(this.KEY, this.state() as string); } });
save(newState: string): void { this.state.set(newState); }
load(): { nodes: Node[]; edges: Edge[]; metadata: Metadata } | null { try { const serializedState = this.state(); return serializedState ? JSON.parse(serializedState) : null; } catch (e) { console.error('SaveStateService: Error loading state', e); return null; } }
clear(): void { this.state.set(null); }}:host { position: relative; width: 100%; height: 100%; display: block; // needed for flex children to work, otherwise children renders outside of host}
.diagram { display: flex; height: var(--ng-diagram-height); border: var(--ng-diagram-border);}:host { position: absolute; top: 1rem; right: 1rem; z-index: 1; background-color: var(--ngd-node-bg-primary-default); border: var(--ng-diagram-border); padding: 1rem;}
.nav-bar { display: flex; gap: 8px; align-items: flex-end; justify-content: end;
& .status { flex: 1; padding: 2px 12px;
&:empty { background: none; padding: 0; } }
button { margin-top: 0; cursor: pointer;
&.clear { background: darkred; border-style: solid; border-color: darkred;
&:disabled { border-color: var(--ngd-node-border-color); } }
&:disabled { cursor: not-allowed;
&.clear { background: none; } } }}Additional Explanation
Section titled “Additional Explanation”Key Concepts
Section titled “Key Concepts”- Persistence: Saves the diagram state to local storage.
- Restoration: Loads the saved state back into the diagram.
- Clear: Removes the saved state from local storage.
Implementation Details
Section titled “Implementation Details”-
Save Persistence Service:
TheSaveStateServicemanages saving, loading, and clearing the diagram state using Angular signals and effects. -
NavBar Component:
The navigation bar provides buttons for saving, loading, and clearing the diagram state. Button states are reactive.
Actions
Section titled “Actions”- Save: Serializes and stores the current diagram state.
- Load: Restores the diagram from the saved state.
- Clear: Removes the saved state from local storage.