Sidebar with Editing Properties
Creating a sidebar for reading and updating node properties in ngDiagram is simple and requires minimal code. This example demonstrates how to build an interactive properties panel that automatically updates when nodes are selected and allows real-time editing of node attributes.
The sidebar is implemented using just a few key concepts:
- Selection Tracking: Uses
NgDiagramSelectionService.selection()
to track selected nodes - Computed Properties: Angular computed signals automatically update the UI when node properties change
- Simple Update Methods: Direct calls to
updateNodeData()
andupdateNode()
for property modifications
How It Works
Section titled “How It Works”The sidebar component automatically:
- Observes the current selection using the model service
- Displays the selected node’s properties (label, resizable, rotatable)
- Updates the node in real-time when properties are changed
- Disables controls when no node is selected
This pattern makes it easy to create property editors for any node attributes you need to expose to users.
import '@angular/compiler';
import { Component } from '@angular/core';import { initializeModel, NgDiagramComponent, provideNgDiagram,} from 'ng-diagram';import { SidebarContainer } from './sidebar.component';
@Component({ imports: [NgDiagramComponent, SidebarContainer], providers: [provideNgDiagram()], template: ` <ng-diagram [model]="model" /> <sidebar-container /> `, styles: ` :host { flex: 1; display: flex; height: 100%;
.coordinates { display: flex; } } `,})export class NgDiagramPropertiesSidebarContainer { model = initializeModel({ metadata: { viewport: { x: -45, y: 80, scale: 0.88 }, }, nodes: [ { id: '1', position: { x: 100, y: 150 }, data: { label: 'Node 1' }, rotatable: true, }, { id: '2', position: { x: 400, y: 150 }, data: { label: 'Node 2' } }, ], edges: [ { id: '1', source: '1', sourcePort: 'port-right', targetPort: 'port-left', target: '2', data: {}, }, ], });}
import '@angular/compiler';
import { Component, computed, inject } from '@angular/core';import { NgDiagramModelService, NgDiagramSelectionService } from 'ng-diagram';
@Component({ selector: 'sidebar-container', templateUrl: `./sidebar.component.html`, styleUrl: './sidebar.component.scss',})export class SidebarContainer { private readonly modelService = inject(NgDiagramModelService); private readonly selectionService = inject(NgDiagramSelectionService); private readonly selectedNode = computed( () => this.selectionService.selection().nodes[0] );
id = computed(() => this.selectedNode().id); label = computed(() => { const data = this.selectedNode()?.data as Data;
return data.label; }); isRotatable = computed(() => this.selectedNode()?.rotatable); isResizable = computed(() => this.selectedNode()?.resizable); isEnabled = computed(() => !!this.selectedNode());
onInput(value: string) { this.modelService.updateNodeData<{ label: string }>(this.id(), { label: value, }); }
onIsResizableChange(value: boolean) { this.modelService.updateNode(this.id(), { resizable: value }); }
onIsRotatableChange(value: boolean) { this.modelService.updateNode(this.id(), { rotatable: value }); }}
type Data = { label: string;};
<div class="sidebar"> <div class="vertical"> <span>Selected node</span> <input [value]="label()" [disabled]="!isEnabled()" (input)="onInput($event.target.value)" /> </div> <div class="horizontal"> <input #isResizableCheckbox type="checkbox" id="isResizable" name="isResizable" [checked]="isResizable()" [disabled]="!isEnabled()" (change)="onIsResizableChange(isResizableCheckbox.checked)" /> <label for="isResizable">Is Resizable</label> </div> <div class="horizontal"> <input #isRotatableCheckbox type="checkbox" id="isRotatable" name="isRotatable" [checked]="isRotatable()" [disabled]="!isEnabled()" (change)="onIsRotatableChange(isRotatableCheckbox.checked)" /> <label for="isRotatable">Is Rotatable</label> </div></div>
:host { position: absolute; height: 100%; padding: 1rem; right: 0;
.sidebar { display: flex; flex-direction: column; background-color: var(--ngd-node-bg-primary-default); border: var(--ngd-node-border-size) solid var(--ngd-node-border-color); border-radius: var(--ngd-node-border-radius); padding: 1rem; gap: 0.5rem; height: 100%; width: 200px; user-select: none;
.horizontal { display: flex; gap: 0.5rem; }
.vertical { display: flex; gap: 0.25rem; flex-direction: column; } }}