Angular Material Node
This example demonstrates how to use Angular Material components in your custom node.
import '@angular/compiler';import { Component } from '@angular/core';import { initializeModel, NgDiagramBackgroundComponent, NgDiagramComponent, NgDiagramNodeTemplateMap, provideNgDiagram, type NgDiagramConfig,} from 'ng-diagram';
import { NodeComponent } from './node/node.component';
enum NodeTemplateType { CustomNodeType = 'customNodeType',}
@Component({ imports: [NgDiagramComponent, NgDiagramBackgroundComponent], providers: [provideNgDiagram()], template: ` <div class="not-content diagram"> <ng-diagram [model]="model" [config]="config" [nodeTemplateMap]="nodeTemplateMap" > <ng-diagram-background /> </ng-diagram> </div> `, styleUrl: './diagram.component.scss',})export class DiagramComponent { nodeTemplateMap = new NgDiagramNodeTemplateMap([ [NodeTemplateType.CustomNodeType, NodeComponent], ]);
config = { zoom: { max: 3, zoomToFit: { onInit: true, padding: 100, }, }, } satisfies NgDiagramConfig;
model = initializeModel({ nodes: [ { id: '1', position: { x: 50, y: 0 }, type: 'customNodeType', data: { name: 'Node 1', description: 'This is Node 1. This node is a custom node with a custom template.', tooltip: 'Node 1 is a custom node', status: 'Active', }, }, { id: '2', position: { x: 400, y: 0 }, type: 'customNodeType', data: { name: 'Node 2', description: 'This is Node 2. Initial status is red. This node is a custom node with a custom template.', tooltip: 'Node 2 is a custom node', status: 'Error', }, }, ], });}import { Component, computed, inject, input, signal } from '@angular/core';import { FormsModule } from '@angular/forms';import { MatChipsModule } from '@angular/material/chips';import { MatExpansionModule } from '@angular/material/expansion';import { MatFormFieldModule } from '@angular/material/form-field';import { MatInputModule } from '@angular/material/input';import { MatSelectModule } from '@angular/material/select';import { NgDiagramModelService, NgDiagramNodeRotateAdornmentComponent, NgDiagramNodeSelectedDirective, type NgDiagramNodeTemplate, type Node,} from 'ng-diagram';
export type NodeData = { name: string; status: string; description: string; tooltip: string;};
@Component({ imports: [ NgDiagramNodeRotateAdornmentComponent, MatSelectModule, MatFormFieldModule, FormsModule, MatInputModule, MatChipsModule, MatExpansionModule, ], hostDirectives: [ { directive: NgDiagramNodeSelectedDirective, inputs: ['node'] }, ], templateUrl: './node.component.html', styleUrls: ['./node.component.scss'],})export class NodeComponent implements NgDiagramNodeTemplate<NodeData> { private readonly modelService = inject(NgDiagramModelService); readonly panelOpenState = signal(false); node = input.required<Node<NodeData>>(); nodeStatus = computed(() => this.node().data.status === 'Active' ? 'green' : this.node().data.status === 'Error' ? 'red' : 'orange' );
onColorChange({ value }: any) { this.modelService.updateNodeData(this.node().id, { ...this.node().data, status: value, }); }}<div class="node"> <div class="node-header"> <div> {{ node().data.name }} </div> <div> <mat-chip [style.backgroundColor]="nodeStatus()"> {{ this.node().data.status }} </mat-chip> </div> </div> <div class="node-body" title="{{ node().data.tooltip }}"> <mat-accordion> <mat-expansion-panel> <mat-expansion-panel-header> <mat-panel-title>Description</mat-panel-title> </mat-expansion-panel-header> <p>{{ node().data.description }}</p> </mat-expansion-panel> <mat-expansion-panel> <mat-expansion-panel-header> <mat-panel-title>Options</mat-panel-title> </mat-expansion-panel-header> <form> <mat-form-field> <mat-select (selectionChange)="onColorChange($event)" [value]="this.node().data.status" name="status" > <mat-option value="Active">Active</mat-option> <mat-option value="Inactive">Inactive</mat-option> <mat-option value="Error">Error</mat-option> </mat-select> </mat-form-field> </form> </mat-expansion-panel> </mat-accordion> </div></div>.diagram { display: flex; height: var(--ng-diagram-height); border: var(--ng-diagram-border);}:host { display: flex; width: 100%; height: 100%; border-radius: 10px; border: 1px solid var(--ngd-node-stroke-primary-default); background-color: var(--ngd-node-bg-primary-default);
.node { display: flex; flex-direction: column; padding: 12px; border-radius: 10px;
&-header { font-weight: bold; margin-bottom: 10px; display: flex; justify-content: space-between; vertical-align: middle; align-items: center; justify-items: center;
.mat-mdc-chip { --mdc-chip-label-text-color: white !important; } }
&-body { font-size: 12px; cursor: help;
p { width: 180px; }
.mat-expansion-panel { --mat-expansion-container-background-color: var( --ngd-node-stroke-primary-default ); --mat-expansion-header-text-color: var(--ngd-txt-primary-default); --mat-expansion-container-text-color: var(--ngd-txt-primary-default); --mat-expansion-header-indicator-color: var(--ngd-txt-primary-default); }
form { height: 50px; margin-top: 8px;
.mat-mdc-form-field { min-width: 120px; } } } }}