Transactions
Transactions in ngDiagram provide a mechanism for batching multiple state changes into atomic operation. This is particularly useful for complex operations that involve multiple nodes and edges, ensuring better performance.
The transaction API is straightforward - simply wrap your operations in a callback.
this.ngDiagramService.transaction(() => { this.ngDiagramModelService.addNodes([node1, node2]); this.ngDiagramModelService.addEdges([edge1]);});How Transactions Work
Section titled “How Transactions Work”Understanding how transactions are applied internally helps you write better code and reason about your diagram state changes. When a transaction is committed, operations are executed in a specific sequence to maintain data integrity:
stateUpdate.nodesToAdd?.forEach((node) => this.addNode(node));stateUpdate.edgesToAdd?.forEach((edge) => this.addEdge(edge));stateUpdate.edgesToRemove?.forEach((id) => this.removeEdge(id));stateUpdate.nodesToRemove?.forEach((id) => this.removeNode(id));stateUpdate.nodesToUpdate?.forEach((node) => this.updateNode(node));stateUpdate.edgesToUpdate?.forEach((edge) => this.updateEdge(edge));
if (stateUpdate.metadataUpdate) { this.metadata = { ...this.metadata, ...stateUpdate.metadataUpdate };}This fixed order ensures that:
- Nodes are added first, making them available before edges that might reference them
- Edges are added next, after all required nodes exist
- Edges are removed before nodes, preventing dangling edge references
- Nodes are removed after edges, ensuring no edges point to non-existent nodes
- Updates are applied after all additions and removals, ensuring the structure is stable before modifying properties
- Metadata is merged last, after all other operations are complete
Benefits
Section titled “Benefits”Performance
Section titled “Performance”Transactions batch multiple operations together, reducing the number of state updates and re-renders. Instead of triggering change detection for each individual operation, all changes are applied at once.
Consistency
Section titled “Consistency”All operations within a transaction succeed together, preventing partial state updates that could leave your diagram in an inconsistent state.
When to Use Transactions
Section titled “When to Use Transactions”Use transactions for:
- Complex multi-step operations - Creating multiple related nodes and edges
- Bulk operations - Adding or updating many elements at once
- Performance optimization - Reducing the number of state updates
Best Practices
Section titled “Best Practices”Group related operations together, but avoid making transactions too large or complex:
// ✅ Good - related operations grouped togetherthis.ngDiagramService.transaction(() => { this.ngDiagramModelService.addNodes([node1, node2, node3]); this.ngDiagramModelService.addEdges([edge1, edge2]);});
// ❌ Avoid - unrelated operations in same transactionthis.ngDiagramService.transaction(() => { this.ngDiagramModelService.addNodes([...]); this.updateUserPreferences(); // Unrelated operation this.updateDiagramName(); // Another unrelated operation});Wrap the entire loop in a single transaction instead of starting one for every iteration to minimize overhead
// ✅ Good - loop inside transactionthis.ngDiagramService.transaction(() => { for (const node of nodes) { this.ngDiagramModelService.updateNodeData(node); }});
// ❌ Avoid - transactions inside a loopfor (const node of nodes) { this.ngDiagramService.transaction(() => { this.ngDiagramModelService.updateNodeData(node); // Each iteration creates a separate transaction });}Example
Section titled “Example”Read more about global configuration in ngDiagram →
import '@angular/compiler';
import { Component, inject } from '@angular/core';import { initializeModel, NgDiagramBackgroundComponent, NgDiagramComponent, NgDiagramModelService, NgDiagramService, provideNgDiagram, type NgDiagramConfig,} from 'ng-diagram';
@Component({ imports: [NgDiagramComponent, NgDiagramBackgroundComponent], providers: [provideNgDiagram()], template: ` <div class="toolbar"> <button (click)="onTestTransactionClick()"> Create diagram (Transaction) </button> <button (click)="onTestWithoutTransactionClick()"> Create diagram (Without Transaction) </button> </div> <div class="not-content diagram"> <ng-diagram [model]="model" [config]="config"> <ng-diagram-background /> </ng-diagram> </div> `, styleUrls: ['./diagram.component.scss'],})export class DiagramComponent { private ngDiagramService = inject(NgDiagramService); private modelService = inject(NgDiagramModelService);
config: NgDiagramConfig = { debugMode: true, };
model = initializeModel({ nodes: [ { id: '1', position: { x: 200, y: 200 }, data: { label: 'Use Buttons to test transaction' }, }, ], });
onTestTransactionClick() { this.cleanDiagram(); this.ngDiagramService.transaction(() => { this.createDiagram('Transaction Node'); }); }
onTestWithoutTransactionClick() { this.cleanDiagram(); this.createDiagram('Non-transaction Node'); }
private createDiagram(nodeName: string) { this.modelService.addNodes([ { id: '1', position: { x: 100, y: 100 }, data: { label: nodeName }, }, { id: '2', position: { x: 100, y: 200 }, data: { label: nodeName }, }, { id: '3', position: { x: 100, y: 300 }, data: { label: nodeName }, }, ]);
this.modelService.updateNodeData('1', { label: `Updated ${nodeName} 1`, }); this.modelService.updateNodeData('2', { label: `Updated ${nodeName} 2`, }); this.modelService.updateNodeData('3', { label: `Updated ${nodeName} 3`, });
this.modelService.addEdges([ { id: 'edge-1', source: '1', target: '2', sourcePort: 'port-right', targetPort: 'port-left', data: {}, }, { id: 'edge-2', source: '2', target: '3', sourcePort: 'port-right', targetPort: 'port-left', data: {}, }, ]); }
private cleanDiagram() { this.modelService.deleteNodes( this.modelService.nodes().map((node) => node.id) ); }}:host { display: flex; position: relative;}
.diagram { display: flex; width: 100%; height: var(--ng-diagram-height); border: var(--ng-diagram-border);}
.toolbar { position: absolute; top: 2rem; right: 1rem; display: flex; gap: 0.5rem; padding: 1rem; justify-content: right; background-color: var(--ngd-node-bg-primary-default); border: var(--ng-diagram-border); z-index: 1;
button { margin-top: 0; }}Whether you’re building simple diagrams or complex applications, transactions help ensure data consistency and optimal performance.