Custom Node
This example demonstrates how to create a custom node with your own template and form controls.
import '@angular/compiler';import { ChangeDetectionStrategy, Component } from '@angular/core';import { initializeModel, NgDiagramComponent, NgDiagramNodeTemplateMap, provideNgDiagram, type NgDiagramConfig,} from 'ng-diagram';
import { NodeComponent } from './node/node.component';
enum NodeTemplateType { CustomNodeType = 'customNodeType',}
@Component({ selector: 'customnode', imports: [NgDiagramComponent], providers: [provideNgDiagram()], changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div class="not-content diagram"> <ng-diagram [model]="model" [config]="config" [nodeTemplateMap]="nodeTemplateMap" /> </div> `, styleUrl: './custom-node.component.scss',})export class CustomNodeComponent { nodeTemplateMap = new NgDiagramNodeTemplateMap([ [NodeTemplateType.CustomNodeType, NodeComponent], ]);
config = { zoom: { max: 3, }, } satisfies NgDiagramConfig;
model = initializeModel({ nodes: [ { id: '1', position: { x: 80, y: 100 }, type: 'customNodeType', data: { name: 'Node 1', description: 'This is Node 1', tooltip: 'Node 1 is a custom node', }, rotatable: true, resizable: true, }, { id: '2', position: { x: 450, y: 100 }, type: 'customNodeType', data: { name: 'Node 2', description: 'This is Node 2', tooltip: 'Node 2 is a custom node', }, rotatable: true, resizable: true, angle: 30, }, ], edges: [ { id: '1', source: '1', target: '2', data: {}, sourcePort: 'port-right', targetPort: 'port-left', sourceArrowhead: 'ng-diagram-arrow', }, ], metadata: { viewport: { x: 0, y: 0, scale: 1 } }, });}
import { Component, input, model } from '@angular/core';import { FormsModule } from '@angular/forms';import { MatChipsModule } from '@angular/material/chips';import { MatFormFieldModule } from '@angular/material/form-field';import { MatInputModule } from '@angular/material/input';import { MatSelectModule } from '@angular/material/select';import { NgDiagramNodeResizeAdornmentComponent, NgDiagramNodeRotateAdornmentComponent, NgDiagramNodeSelectedDirective, NgDiagramPortComponent, type NgDiagramNodeTemplate, type Node,} from 'ng-diagram';
@Component({ selector: 'node', imports: [ NgDiagramNodeRotateAdornmentComponent, NgDiagramPortComponent, NgDiagramNodeResizeAdornmentComponent, MatSelectModule, MatFormFieldModule, FormsModule, MatInputModule, MatChipsModule, ], templateUrl: './node.component.html', styleUrls: ['./node.component.scss'], hostDirectives: [ { directive: NgDiagramNodeSelectedDirective, inputs: ['node'] }, ], host: { '[class.ng-diagram-port-hoverable-over-node]': 'true', },})export class NodeComponent implements NgDiagramNodeTemplate { text = model<string>(''); node = input.required<Node>();
selectedState: string = 'Active';}
<ng-diagram-node-rotate-adornment /><ng-diagram-node-resize-adornment> <div class="node"> <div class="node-header"> <div> {{ node().data.name }} </div> <div> <mat-chip>{{ selectedState }}</mat-chip> </div> </div> <div class="node-body" title="{{ node().data.tooltip }}"> {{ node().data.description }} <form> <mat-form-field> <mat-select [(ngModel)]="selectedState" name="state"> <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> </div> </div> <ng-diagram-port id="port-left" type="both" side="left" /> <ng-diagram-port id="port-top" type="both" side="top" /> <ng-diagram-port id="port-right" type="both" side="right" /> <ng-diagram-port id="port-bottom" type="both" side="bottom" /></ng-diagram-node-resize-adornment>
:host { flex: 1; display: flex;}
.diagram { flex: 1; display: flex; height: 20rem; font-family: 'Poppins', sans-serif;}
:host { --mdc-chip-label-text-size: 10px; --mat-form-field-container-vertical-padding: 8px; --mat-form-field-container-height: 30px;
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: 4px; display: flex; justify-content: space-between; vertical-align: middle; align-items: center; justify-items: center;
.mat-mdc-chip { font-size: 11px; height: 20px; margin-bottom: 10px; } }
&-body { font-size: 12px; cursor: help;
form { height: 50px; margin-top: 8px;
.mat-mdc-form-field { min-width: 120px; } } }
&-button { margin-top: 8px; padding: 4px 8px; font-size: 12px; border: 1px solid var(--ngd-node-stroke-primary-default); border-radius: 4px; cursor: pointer;
&:hover { border-color: var(--ngd-node-stroke-primary-hover); } } }}