Skip to content

Standalone Component - #622

Merged
steveblue merged 3 commits into
angular-upgrade-21from
ngx-graph-major-changes-21
May 11, 2026
Merged

Standalone Component#622
steveblue merged 3 commits into
angular-upgrade-21from
ngx-graph-major-changes-21

Conversation

@steveblue

@steveblue steveblue commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce? (check one with "x")

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the new behavior?

  • Breaking: GraphComponent is now a standalone component built on signal APIs (input, output, model, contentChild, viewChildren) instead of decorator-based @Input / @Output and classic queries. The published NgxGraphModule and GraphModule wrappers are still there so existing NgModule-based applications can import the graph as before, but those modules are deprecated; new code should import GraphComponent (and add it to the consuming component or route imports array) the same way you would any other standalone piece.
  • Breaking: From TypeScript, inputs behave like signal readers: for example, read the current node list with graph.nodes() rather than graph.nodes. The layout, curve, and activeEntries bindings use model() where the graph needs to write back internally; consumers generally keep using normal property bindings, while code inside the component updates via .set().
  • Breaking: Outputs are OutputRef values, not EventEmitter, so they do not offer .pipe(). To keep using RxJS operators, turn an output into an observable with outputToObservable from @angular/core/rxjs-interop (in an appropriate injection context), or subscribe to the ref directly. Payload types (such as NgxGraphStateChangeEvent) are unchanged; only the subscription shape differs.
  • Breaking: @ViewChildren / @ContentChild style access is replaced with signal query functions, so in component code you call graph.linkElements() (or the other query methods) instead of keeping a QueryList on a property.
  • Breaking: The in-component animations: [ trigger(...) ] definition was removed. Enter styling on the root now uses the framework’s animate.enter class binding together with keyframes in the graph stylesheet, which replaces the old opacity :enter transition and avoids the deprecated trigger API. Angular does not support mixing the legacy animations metadata and animate.enter / animate.leave on the same component. The old [@.disabled] host binding for the legacy tree is also gone; it was scoped to the previous animation system and did not drive node positions, but projected templates that relied on that subtree being disabled for their own [@...] triggers may need a different approach.
  • Breaking: Optional viewport inputs zoomLevel, panOffsetX, and panOffsetY are now signal inputs wired through effect() to zoomTo and panTo, so the graph no longer assigns those via a property setter, and the effects run when the parent binding’s value actually changes. If a parent expression changes on every data refresh, you may see extra viewport updates; binding stable values (or leaving these inputs unset) matches the old mental model. Automatic fit and centering still come from the documented autoZoom, autoCenter, and zoomToFit$ behavior. If you use transitionAfterChanges with full-scope layout morph, whether the post-tick block runs zoomToFit or center can differ from the additive or instant paths, as before.
  • Enhancement: transitionAfterChanges, transitionDuringTransform, layoutTransitionEffect, and applyVisualContinuityBeforeLayout inputs configure rich continuity when a prior graph had nodes, allowing for animations between graph updates.
  • Breaking: useLayoutTransitions (default true controls layout-js-driven; when false it applies only during JS mode: 'tween');
  • Enhancement: GraphComponent edgePathSampleCount input (default 48, clamped 2–512) for edge resampling in layout, morph, and redrawEdge
  • Enhancement: transitionAfterChanges.morphCapture for prior translate source (model vs main-chart DOM, optional model fallback) and syncTargetsFromPositionAfterTick / snapAddedNodeIds; mergeGraphLayoutTransition merges morphCapture with defaults
  • Enhancement: drawComplete emits after a completed draw/tick pass (paths bound, graph ready). Use to hide loading UI or run one-shot center/zoomToFit without flashing before first layout.
  • Enhancement: Minimap can be displayed on bottom.
  • Fix: Observable layouts (Cola, D3 force) faster than afterNextRender—superseded passes repaint link paths from the current model without full redrawLines so layout morph is not cancelled.
  • Fix: Layout morph keeps oldLine / oldTextPath on paths that are not resampled-tweening until the tween ends instead of snapping to the new route early.
  • Fix: Drag updateEdge for Dagre and DagreCluster uses orientation-aware paths aligned with DagreNodesOnly.
  • Chore: Update Storybook with documentation, examples

Does this PR introduce a breaking change? (check one with "x")

  • Yes
  • No

Note

High Risk
High risk because it introduces breaking public API changes (standalone component, signal-based inputs/outputs/queries) and rewrites core rendering/animation logic for node/edge layout transitions and viewport transforms.

Overview
Migrates ngx-graph to Angular 20+ standalone + signals. GraphComponent switches from decorator-based @Input/@Output + EventEmitter + @ViewChildren/@ContentChild to signal APIs (input/model/output, viewChildren/contentChild), and GraphModule/NgxGraphModule are retained but deprecated and updated to import standalone artifacts.

Reworks rendering + transitions. Removes legacy @angular/animations triggers in favor of animate.enter + CSS keyframes, adds JS-driven layout morphing via new transition inputs (transitionAfterChanges, useLayoutTransitions, edgePathSampleCount, viewport pan easing, optional layout “flair” effect), and adds a drawComplete output emitted once paths are bound and dims are ready.

Improves edge/minimap behavior and adds new utilities/tests. Introduces shared edge-geometry helpers for Dagre/ELK drag routing, extends minimap positioning (upper/lower corners) with margins and corrected pan-to math, adjusts minimap styling, and adds extensive unit tests covering transition merging, drag edge geometry, redraw/resampling, and drawComplete.

Reviewed by Cursor Bugbot for commit 6d503b2. Bugbot is set up for automated code reviews on this repo. Configure here.

… `layoutTransitionEffect`, and `applyVisualContinuityBeforeLayout` inputs configure rich continuity when a prior graph had nodes, allowing for animations between graph updates.

- Breaking: `useLayoutTransitions` (default `true` controls `layout-js-driven`; when `false` it applies only during JS `mode: 'tween'`);
- Enhancement: `GraphComponent` `edgePathSampleCount` input (default 48, clamped 2–512) for edge resampling in layout, morph, and `redrawEdge`
- Enhancement: `transitionAfterChanges.morphCapture` for prior translate source (model vs main-chart DOM, optional model fallback) and `syncTargetsFromPositionAfterTick` / `snapAddedNodeIds`; `mergeGraphLayoutTransition` merges `morphCapture` with defaults
- Enhancement: `drawComplete` emits after a completed draw/tick pass (paths bound, graph ready). Use to hide loading UI or run one-shot center/zoomToFit without flashing before first layout.
- Enhancement: Minimap can be displayed on bottom.
- Fix: Observable layouts (Cola, D3 force) faster than `afterNextRender`—superseded passes repaint link paths from the current model without full `redrawLines` so layout morph is not cancelled.
- Fix: Layout morph keeps `oldLine` / `oldTextPath` on paths that are not resampled-tweening until the tween ends instead of snapping to the new route early.
- Fix: Drag `updateEdge` for Dagre and DagreCluster uses orientation-aware paths aligned with DagreNodesOnly.
- Chore: Update Storybook with documentation, examples
Comment thread projects/swimlane/ngx-graph/src/lib/graph/graph.component.ts
Comment thread projects/swimlane/ngx-graph/src/lib/graph/layouts/edge-geometry.ts
></ng-container>
}
@if (!linkTemplate()) {
<svg:path class="edge line" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default link path has no initial d attribute

Medium Severity

The default link template <svg:path class="edge line" /> no longer binds [attr.d]="link.line" (the old template had [attr.d]="link.line"). The d attribute is now set imperatively by D3 in redrawLines, which runs after afterNextRender. This leaves default-template edge paths without geometry for at least one rendered frame, causing a visible flash where edges are invisible before D3 paints them.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5501254. Configure here.

- fix: blocker for drawing minimap in some instances
- fix: restyle minimap colors
- fix: drawComplete should fire when all nodes are ready

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6d503b2. Configure here.

Comment thread projects/swimlane/ngx-graph/src/lib/graph/graph.component.ts
Comment thread projects/swimlane/ngx-graph/src/lib/graph/graph.component.ts
@steveblue
steveblue requested a review from jogaj April 23, 2026 15:37
@steveblue
steveblue merged commit c4d51ef into angular-upgrade-21 May 11, 2026
2 checks passed
steveblue added a commit that referenced this pull request May 14, 2026
## 12.0.0-alpha.2

- Breaking: `draggingEnabled`, `panningEnabled` renamed to `enableDrag` and `enablePan`.
- Breaking: `draggingEnabled`, `panningEnabled`, `enableZoom` converted to model.
- Enhancement: `setViewportInteraction` allows toggling pan, drag, and zoom in one imperative method.
- Fix: `CompoundNode` and `Cluster` should transition size and position when animations are enabled.
- Fix: `tick()` assigns `oldNodes` / `oldClusters` / `oldCompoundNodes` from the current graph **before** the post-tick `requestAnimationFrame`, so `[class.old-node]` is consistent for newly added ids on the same tick (fixes flash on zoom with `mode: 'tween'`, including additive + `snapAddedNodeIds`).
- Fix: `#nodeTemplate` / `#linkTemplate` / `#clusterTemplate` outlets reuse a stable `ngTemplateOutletContext` object per graph id (pruned each `tick()`) so consumer templates (e.g. tooltips) are not torn down and recreated on viewport-only updates such as zoom.
- Fix: `applyVisualContinuityBeforeLayout` clears `hidden` on nodes/clusters/compound nodes whose id existed in the prior graph so host-owned `[nodes]` references do not keep stale `hidden: true` after `tick()` ran on a different layout object graph (reduces template blink when `update()` runs again).

## 12.0.0-alpha.1

- Breaking: `GraphComponent` is now a standalone component built on signal APIs (`input`, `output`, `model`, `contentChild`, `viewChildren`) instead of decorator-based `@Input` / `@Output` and classic queries. The published `NgxGraphModule` and `GraphModule` wrappers are still there so existing NgModule-based applications can import the graph as before, but those modules are deprecated; new code should import `GraphComponent` (and add it to the consuming component or route `imports` array) the same way you would any other standalone piece.
- Breaking: From TypeScript, inputs behave like signal readers: for example, read the current node list with `graph.nodes()` rather than `graph.nodes`. The `layout`, `curve`, and `activeEntries` bindings use `model()` where the graph needs to write back internally; consumers generally keep using normal property bindings, while code inside the component updates via `.set()`.
- Breaking: Outputs are `OutputRef` values, not `EventEmitter`, so they do not offer `.pipe()`. To keep using RxJS operators, turn an output into an observable with `outputToObservable` from `@angular/core/rxjs-interop` (in an appropriate injection context), or subscribe to the ref directly. Payload types (such as `NgxGraphStateChangeEvent`) are unchanged; only the subscription shape differs.
- Breaking: `@ViewChildren` / `@ContentChild` style access is replaced with signal query functions, so in component code you call `graph.linkElements()` (or the other query methods) instead of keeping a `QueryList` on a property.
- Breaking: The in-component `animations: [ trigger(...) ]` definition was removed. Enter styling on the root now uses the framework’s `animate.enter` class binding together with keyframes in the graph stylesheet, which replaces the old opacity `:enter` transition and avoids the deprecated `trigger` API. Angular does not support mixing the legacy `animations` metadata and `animate.enter` / `animate.leave` on the same component. The old `[@.disabled]` host binding for the legacy tree is also gone; it was scoped to the previous animation system and did not drive node positions, but projected templates that relied on that subtree being disabled for their own `[@...]` triggers may need a different approach.
- Breaking: Optional viewport inputs `zoomLevel`, `panOffsetX`, and `panOffsetY` are now signal inputs wired through `effect()` to `zoomTo` and `panTo`, so the graph no longer assigns those via a property setter, and the effects run when the parent binding’s value actually changes. If a parent expression changes on every data refresh, you may see extra viewport updates; binding stable values (or leaving these inputs unset) matches the old mental model. Automatic fit and centering still come from the documented `autoZoom`, `autoCenter`, and `zoomToFit$` behavior. If you use `transitionAfterChanges` with full-scope layout morph, whether the post-tick block runs `zoomToFit` or `center` can differ from the additive or instant paths, as before.
- Breaking: If node.position is undefined, could cause a template error, ensure node has position via layout or proper handling in template.
- Breaking: When using new transitions, any condition wrappers in Angular templates for node and edges could interfere with animations if the condition wraps a container node.
- Enhancement: `transitionAfterChanges`, `transitionDuringTransform`, `layoutTransitionEffect`, and `applyVisualContinuityBeforeLayout` inputs configure rich continuity when a prior graph had nodes, allowing for animations between graph updates.
- Breaking: `useLayoutTransitions` (default `true` controls `layout-js-driven`; when `false` it applies only during JS `mode: 'tween'`);
- Enhancement: `GraphComponent` `edgePathSampleCount` input (default 48, clamped 2–512) for edge resampling in layout, morph, and `redrawEdge`
- Enhancement: `transitionAfterChanges.morphCapture` for prior translate source (model vs main-chart DOM, optional model fallback) and `syncTargetsFromPositionAfterTick` / `snapAddedNodeIds`; `mergeGraphLayoutTransition` merges `morphCapture` with defaults
- Enhancement: `drawComplete` emits after a completed draw/tick pass (paths bound, graph ready). Use to hide loading UI or run one-shot center/zoomToFit without flashing before first layout.
- Enhancement: Minimap can be displayed on bottom.
- Fix: Observable layouts (Cola, D3 force) faster than `afterNextRender`—superseded passes repaint link paths from the current model without full `redrawLines` so layout morph is not cancelled.
- Fix: Layout morph keeps `oldLine` / `oldTextPath` on paths that are not resampled-tweening until the tween ends instead of snapping to the new route early.
- Fix: Drag `updateEdge` for Dagre and DagreCluster uses orientation-aware paths aligned with DagreNodesOnly.
- Chore: Update Storybook with documentation, examples

## 12.0.0-alpha.0

- Enhancement: Support for Angular 21
- Chore: Upgrade storybook to v10


* Upgrade prettier and pretty-quick

* Upgrade storybook

* Upgrade storybook v2

* Fix after project upgrade

* Removing yarn.lock

* Upgrading swimlane/prettier-config-swimlane

* Updating versions

* Yarn lock

* Standalone Component (#622)

- Enhancement: `transitionAfterChanges`, `transitionDuringTransform`, `layoutTransitionEffect`, and `applyVisualContinuityBeforeLayout` inputs configure rich continuity when a prior graph had nodes, allowing for animations between graph updates.
- Breaking: `useLayoutTransitions` (default `true` controls `layout-js-driven`; when `false` it applies only during JS `mode: 'tween'`);
- Enhancement: `GraphComponent` `edgePathSampleCount` input (default 48, clamped 2–512) for edge resampling in layout, morph, and `redrawEdge`
- Enhancement: `transitionAfterChanges.morphCapture` for prior translate source (model vs main-chart DOM, optional model fallback) and `syncTargetsFromPositionAfterTick` / `snapAddedNodeIds`; `mergeGraphLayoutTransition` merges `morphCapture` with defaults
- Enhancement: `drawComplete` emits after a completed draw/tick pass (paths bound, graph ready). Use to hide loading UI or run one-shot center/zoomToFit without flashing before first layout.
- Enhancement: Minimap can be displayed on bottom.
- Fix: Observable layouts (Cola, D3 force) faster than `afterNextRender`—superseded passes repaint link paths from the current model without full `redrawLines` so layout morph is not cancelled.
- Fix: Layout morph keeps `oldLine` / `oldTextPath` on paths that are not resampled-tweening until the tween ends instead of snapping to the new route early.
- Fix: Drag `updateEdge` for Dagre and DagreCluster uses orientation-aware paths aligned with DagreNodesOnly.
- Chore: Update Storybook with documentation, examples
- Enhancement: upgrade component to latest
- Enhancement: give minimap an optional margin
- Fix: blocker for drawing minimap in some instances
- Fix: restyle minimap colors
- Fix: drawComplete should fire when all nodes are ready

* (release): 21.0.0-alpha.1

* Animation and Interactivity Fixes (#623)

* fix: cluster and compound node animation
* fix: toggle zoom, pan, drag
* fix: new nodes were hanging onto hidden flag
* fix: run tests in ci
* fix: package

* (release): 12.0.0-alpha.2

---------

Co-authored-by: Jonathan Avendano <jogaj_26@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants