Custom Ports Example
This example demonstrates how to create custom ports in ngDiagram by providing any custom content (text, images, SVGs, or Angular components) and customizing their appearance and behavior.
import '@angular/compiler';import { Component } from '@angular/core';import { initializeModel, NgDiagramBackgroundComponent, NgDiagramComponent, NgDiagramModelService, provideNgDiagram, type Edge, type NgDiagramConfig, type NgDiagramNodeTemplateMap,} from 'ng-diagram';import { HeaderNodeComponent } from './header-node/header-node.component';import { SocketNodeComponent } from './socket-node/socket-node.component';
@Component({ imports: [NgDiagramComponent, NgDiagramBackgroundComponent], providers: [NgDiagramModelService, 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: NgDiagramNodeTemplateMap = new Map([ ['headerNodeType', HeaderNodeComponent], ['socketNodeType', SocketNodeComponent], ]);
config = { zoom: { max: 3, zoomToFit: { onInit: false, }, }, edgeRouting: { orthogonal: { maxCornerRadius: 0, }, }, linking: { finalEdgeDataBuilder: (edge: Edge) => ({ ...edge, targetArrowhead: undefined, }), }, zIndex: { edgesAboveConnectedNodes: true, }, } satisfies NgDiagramConfig;
model = initializeModel({ metadata: { viewport: { x: 200, y: 50, scale: 1 } }, nodes: [ { id: '1', position: { x: 0, y: 0 }, autoSize: false, size: { width: 200, height: 400 }, type: 'headerNodeType', data: { name: 'IC 111111', }, }, { id: '2', position: { x: 300, y: 140 }, autoSize: false, angle: 90, size: { width: 250, height: 80 }, type: 'socketNodeType', data: { name: 'Socket 01', }, }, ], edges: [ { id: 'abf5265b-c5c3-45af-a382-77776faa65aa', data: {}, source: '2', sourcePort: 'port-bottom-1', target: '1', targetPort: 'port-c1plus', }, { id: 'ffde2e9d-3fd8-4463-b570-ff9b3d3ec6ac', data: {}, source: '2', sourcePort: 'port-bottom-2', target: '1', targetPort: 'port-r3in', }, { id: '96b05c41-c4bb-4c65-a737-dc0e480cc15e', data: {}, source: '2', sourcePort: 'port-bottom-3', target: '1', targetPort: 'port-r4in', }, { id: 'c74539f0-1ab5-4bf7-a0af-49e194ed8063', data: {}, source: '2', sourcePort: 'port-bottom-4', target: '1', targetPort: 'port-t3out', }, { id: 'd86312f4-4269-4006-bd1f-3d9bbbe49cee', data: {}, source: '2', sourcePort: 'port-bottom-5', target: '1', targetPort: 'port-t1out', }, ], });}import { Component, computed, input } from '@angular/core';import { NgDiagramPortComponent, type NgDiagramNodeTemplate, type Node,} from 'ng-diagram';
@Component({ imports: [NgDiagramPortComponent], templateUrl: './header-node.component.html', styleUrls: ['./header-node.component.scss'], host: { '[class.ng-diagram-port-hoverable]': 'true', },})export class HeaderNodeComponent implements NgDiagramNodeTemplate<Data> { text = computed(() => this.node()?.data?.name || ''); node = input.required<Node<Data>>();}
type Data = { name: string;};import { Component, computed, input } from '@angular/core';import { NgDiagramPortComponent, type NgDiagramNodeTemplate, type Node,} from 'ng-diagram';
@Component({ imports: [NgDiagramPortComponent], templateUrl: './socket-node.component.html', styleUrls: ['./socket-node.component.scss'], host: { '[class.ng-diagram-port-hoverable]': 'true', },})export class SocketNodeComponent implements NgDiagramNodeTemplate<Data> { text = computed(() => this.node()?.data?.name || ''); node = input.required<Node<Data>>();}
type Data = { name: string;};<div class="header-node"> <div class="header-node__title">{{ text() }}</div> <!-- Left ports --> <ng-diagram-port id="port-en" type="both" side="left" style="top: 20%" originPoint="centerLeft" > <div class="header-node-port-content header-node-port"> <svg width="30" height="30" viewBox="1 0 32 32"> <circle cx="15" cy="15" r="13" fill="transparent" stroke-width="1" /> </svg> <span>EN</span> </div> </ng-diagram-port> <ng-diagram-port id="port-r3out" type="both" side="left" style="top: 33%" originPoint="centerLeft" > <div class="header-node-port">R3OUT</div> </ng-diagram-port> <ng-diagram-port id="port-r4out" type="both" side="left" style="top: 58%" originPoint="centerLeft" > <div class="header-node-port">R4OUT</div> </ng-diagram-port> <ng-diagram-port id="port-t3in" type="both" side="left" style="top: 70%" originPoint="centerLeft" > <div class="header-node-port">T3IN</div> </ng-diagram-port> <!-- Right ports --> <ng-diagram-port id="port-c1plus" type="both" side="right" style="top: 20%" originPoint="centerRight" > <div class="header-node-port">C1+</div> </ng-diagram-port> <ng-diagram-port id="port-r3in" type="both" side="right" style="top: 33%" originPoint="centerRight" > <div class="header-node-port">R3IN</div> </ng-diagram-port> <ng-diagram-port id="port-r4in" type="both" side="right" style="top: 45%" originPoint="centerRight" > <div class="header-node-port">R4IN</div> </ng-diagram-port> <ng-diagram-port id="port-t3out" type="both" side="right" style="top: 58%" originPoint="centerRight" > <div class="header-node-port">T3OUT</div> </ng-diagram-port> <ng-diagram-port id="port-t1out" type="both" side="right" style="top: 70%" originPoint="centerRight" > <div class="header-node-port" style="width: 35px; height: 35px; margin-top: 2px; margin-bottom: 2px" > <img alt="" class="theme-img-light" src="/docs/assets/examples/ground-icon.svg" /> <img alt="" class="theme-img-dark" src="/docs/assets/examples/ground-icon-dark.svg" /> </div> </ng-diagram-port> <!-- top ports --> <ng-diagram-port id="port-v-plus" type="both" side="top" style="" originPoint="topCenter" > <div style="padding: 0.25rem 0">V<sub>+</sub></div> </ng-diagram-port> <!-- bottom ports --> <ng-diagram-port id="port-v-minus" type="both" side="bottom" style="" originPoint="bottomCenter" > <div style="padding: 0.25rem 0">V<sub>-</sub></div> </ng-diagram-port></div><div style="position: relative; width: 250px; height: 80px"> <svg width="250" height="80" style="position: absolute; top: 0; left: 0"> <polygon points="0,80 250,80 230,0 20,0" /> </svg> <span style=" position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-weight: bold; " > {{ text() }} </span></div><ng-diagram-port id="port-bottom-1" type="both" style="top: 75%; left: 10%" side="bottom"/><ng-diagram-port id="port-bottom-2" type="both" style="top: 25%; left: 31%" side="bottom"/><ng-diagram-port id="port-bottom-3" type="both" style="top: 75%; left: 50%" side="bottom"/><ng-diagram-port id="port-bottom-4" type="both" style="top: 25%; left: 71%" side="bottom"/><ng-diagram-port id="port-bottom-5" type="both" style="top: 75%; left: 90%" side="bottom"/>.diagram { display: flex; height: var(--ng-diagram-height); border: var(--ng-diagram-border);}:host { display: flex; width: 100%; height: 100%; background-color: var(--ngd-node-bg-primary-default); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); border: 1px solid #383838; color: var(--ngd-txt-primary-default);
/* Override port CSS variables for custom styling */ --ngd-port-border-size: 0px; --ngd-port-background-color: transparent; --ngd-port-border-size-hover: 0px;
.header-node-port { font-size: 0.75rem; margin: 0 0.5rem; }
.header-node-port-content { display: flex; flex-direction: row; justify-content: center; align-items: center;
svg { stroke: var(--ngd-txt-primary-default); } }
.header-node { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 100%; height: 100%; & img { pointer-events: none; }
.theme-img-light { display: block; } .theme-img-dark { display: none; }
&__title { font-weight: bold; margin-bottom: 2rem; } }}
:host-context([data-theme='dark']) { .header-node { .theme-img-light { display: none; } .theme-img-dark { display: block; } }}:host { display: flex; width: 100%; height: 100%; background-color: transparent; color: var(--ngd-txt-primary-default);
--ngd-node-bg-primary-default: transparent; --ngd-port-border-size: 2px; --ngd-port-border-size-hover: 0px; --ngd-port-size: 10px; --ngd-port-background-color: transparent;
svg { fill: var(--ngd-ui-bg-primary-default); stroke: var(--ngd-pt-stroke-primary-default); }}Additional Explanation
Section titled “Additional Explanation”Key Concepts
Section titled “Key Concepts”-
Custom Port Content:
You can provide any custom content inside a port. The default style is disabled and the port adapts to the content’s size and shape. -
Custom Port Styling:
Ports can be styled individually using CSS classes, inline styles, or by overriding CSS variables used in port styling. -
Dynamic Port Rendering:
Ports are rendered using the<ng-diagram-port>component, which supports various attributes. -
Node Template Customization:
TheNodeComponenttemplate defines the arrangement and styling of ports within each node. -
Custom Content Rendering:
You can render any content inside a port, such as text, images, SVGs, or icons. This allows for full control over the port’s appearance. -
Port Binding:
Ports have atypeproperty to set the connection type for each port. You can set it to'both','target', or'source'. -
Port Positioning:
Ports have asideproperty to set their placement. -
Origin Point Customization:
You can now use theoriginPointinput to precisely control the transform origin of each port.
Supported values:leftTop,leftMiddle,leftBottom,centerTop,centerMiddle,centerBottom,rightTop,rightMiddle,rightBottom.This will apply the corresponding CSS transform for accurate port placement.
For details, see the
NgDiagramPortComponentdocumentation.
To further customize port placement, you can use CSS styling.