@bravobit/ng-org-chart
v1.0.2
Published
Angular wrapper around @bravobit/org-chart: declarative org/ownership charts with template-based nodes.
Downloads
63
Readme
@bravobit/ng-org-chart
Angular wrapper around @bravobit/org-chart: declarative organization/ownership charts where every node is an Angular template.
<ng-org-chart>is the chart: it receives thenodesandedgesand hosts the viewport (pan, zoom, hover highlighting, keyboard navigation)*ngOrgChartNodedeclares — pernode.type— the template and dimensions of that node type- Signal-based and zoneless-ready; re-renders automatically when
nodes,edgesoroptionschange - Full SSR support: on the server the chart renders through a
Renderer2-backed DOM adapter, so the SVG is part of the HTML response (scaled viaviewBox); the browser then rebuilds it interactively (ngSkipHydration)
Installation
npm install @bravobit/ng-org-chartThe core engine @bravobit/org-chart is a regular dependency and is installed automatically; its types are re-exported by this package.
Add the chart stylesheet (shipped with this package) to your global styles, e.g. in angular.json:
"styles": [
"src/styles.scss",
"node_modules/@bravobit/ng-org-chart/org-chart.css"
]Quick start
import { Component, signal } from '@angular/core';
import { NgOrgChart, NgOrgChartNode, type OrgChartEdge, type OrgChartNode } from '@bravobit/ng-org-chart';
interface EntityData {
label: string;
}
@Component({
selector: 'app-structure',
imports: [NgOrgChart, NgOrgChartNode],
template: `
<ng-org-chart [nodes]="nodes()" [edges]="edges()" style="height: 600px">
<div *ngOrgChartNode="'company'; typedBy: nodes(); width: 220; height: 72; let node" class="card">
{{ node.data.label }}
</div>
<div *ngOrgChartNode="'person'; typedBy: nodes(); width: 180; height: 56; let node" class="card card--person">
{{ node.data.label }}
</div>
</ng-org-chart>
`,
})
export class Structure {
protected readonly nodes = signal<OrgChartNode<EntityData>[]>([
{ id: 'bv', type: 'company', data: { label: 'Meridiaan B.V.' } },
{ id: 'holding', type: 'company', data: { label: 'Holding B.V.' } },
{ id: 'ubo', type: 'person', data: { label: 'A. de Groot' } },
]);
// Edges point from the owned entity up to its owner ('up' is the default).
protected readonly edges = signal<OrgChartEdge[]>([
{ from: 'bv', to: 'holding', label: '100%', arrow: 'from' },
{ from: 'holding', to: 'ubo', label: '100%', arrow: 'from' },
]);
}Every type that appears in nodes needs exactly one *ngOrgChartNode template. The template receives the placed node as $implicit (let node) and its payload as data (let data = data).
<ng-org-chart> (NgOrgChart)
| Input | Type | Description |
| --- | --- | --- |
| nodes | OrgChartNode<TData>[] | Required. The chart's nodes. |
| edges | OrgChartEdge[] | The edges; direction defaults to 'up' (toward the owner/parent). |
| options | DeepPartial<OrgChartOptions> | Options patch (orientation, gaps, zoom, hover, ariaLabel, …). |
| autoFit | 'first' \| 'always' \| 'none' | When to scale/center automatically. Default 'first'. |
| highlight | HighlightResolver<TData> | Custom hover highlighting (default: direct neighbors). |
| Output | Payload | Description |
| --- | --- | --- |
| nodeClick | { node, event } | Click or Enter/Space on a node. |
| nodeHover | { node \| null, event } | Hovered/focused node changed. |
| edgeClick | { edge, event } | Click on an edge or its label. |
| edgeHover | { edge \| null, event } | Hovered edge changed. |
Methods (via viewChild(NgOrgChart)): fit(padding?), zoomIn(), zoomOut(), and the chartInstance getter as escape hatch to the underlying OrgChart.
The host element is the chart container — give it a size (height, flex, …).
*ngOrgChartNode (NgOrgChartNode)
<div *ngOrgChartNode="'company'; width: 220; height: 72; typedBy: nodes(); let node; let data = data">
…
</div>| Key | Description |
| --- | --- |
| (expression) | The node.type this template renders. |
| width / height | Required. Dimensions in px of nodes of this type — the layout needs sizes up front; nothing is measured. |
| typedBy | Optional, type-inference only: pass your nodes array to type node.data in the template. |
Context: $implicit is the PlacedNode (id, type, data, position, placement, hasParent, …); data is a shortcut for node.data.
SSR
The chart renders on the server too — no extra setup needed. All DOM mutations go through a DomAdapter backed by Angular's Renderer2, so the same code path works on every Angular platform. Because the server cannot measure layout, the static SVG is scaled and centered with a viewBox capped at fitMaxZoom — the SSR equivalent of fit(). The component is marked ngSkipHydration: the server-rendered chart serves as the first paint, after which the browser rebuilds it interactively with real pan/zoom fitting.
Styling
Nodes are your own markup — style them however you want; a node's root element best fills its box (height: 100%). Chart colors (edges, labels, focus ring, fade) are tuned with CSS variables on the container, see the core documentation. Type-specific hooks are also available on the SVG side as .ogc-node--<type>.
Playground
This repository contains a playground application demonstrating everything (owners, percentages, side branches, cross links, floating nodes, dynamic data, orientation switching):
npm start