What happens
A "dangling" edge — one end is a node/port, the other is a free point defined only by targetPosition (target: '') — works when added at runtime via addEdges, but when the same edge is seeded through initializeModel, its targetPosition is discarded. The edge then has no resolvable endpoint, so ng-diagram logs Invalid edge coordinates detected on every routing pass and the edge can't be routed.
Both edges are dangling and identical except that, after initializeModel, restored re-applies its targetPosition via model.updateEdges while broken does not.
- Open the console on load → one error:
Invalid edge coordinates detected for edge 'broken'. broken renders nothing; restored renders fine.
- Drag the Source node →
broken re-logs the error on each routing pass; restored follows the node and stays silent.
The post-init updateEdges call is the only difference, which isolates the cause.
Cause
initialize-model.ts#L100 runs every edge through stripEdgeRuntimeProperties, defined at strip-runtime-properties.ts#L27-L33, which removes sourcePosition and targetPosition (along with measuredLabels, computedZIndex, _internalId).
For a connected edge this is harmless — routing recomputes the endpoints from the measured ports. But for a dangling edge, targetPosition/sourcePosition is the only source of truth for the free end, so stripping it removes authored data. Note the asymmetry: addEdges/updateEdges don't strip, which is why runtime-created dangling edges work.
What I'd like
Stripping computed values on init is reasonable in general (the doc comment is right — stale measuredLabels/computedZIndex from a persisted model should be dropped). But it shouldn't throw away the authored free-end position of a dangling edge. I'd like some supported way to define/persist a model that contains dangling edges, so they survive toJSON → initializeModel (which currently both strip) and can be reloaded.
What happens
A "dangling" edge — one end is a node/port, the other is a free point defined only by
targetPosition(target: '') — works when added at runtime viaaddEdges, but when the same edge is seeded throughinitializeModel, itstargetPositionis discarded. The edge then has no resolvable endpoint, so ng-diagram logsInvalid edge coordinates detectedon every routing pass and the edge can't be routed.Repro: StackBlitz
Both edges are dangling and identical except that, after
initializeModel,restoredre-applies itstargetPositionviamodel.updateEdgeswhilebrokendoes not.Invalid edge coordinates detected for edge 'broken'.brokenrenders nothing;restoredrenders fine.brokenre-logs the error on each routing pass;restoredfollows the node and stays silent.The post-init
updateEdgescall is the only difference, which isolates the cause.Cause
initialize-model.ts#L100runs every edge throughstripEdgeRuntimeProperties, defined atstrip-runtime-properties.ts#L27-L33, which removessourcePositionandtargetPosition(along withmeasuredLabels,computedZIndex,_internalId).For a connected edge this is harmless — routing recomputes the endpoints from the measured ports. But for a dangling edge,
targetPosition/sourcePositionis the only source of truth for the free end, so stripping it removes authored data. Note the asymmetry:addEdges/updateEdgesdon't strip, which is why runtime-created dangling edges work.What I'd like
Stripping computed values on init is reasonable in general (the doc comment is right — stale
measuredLabels/computedZIndexfrom a persisted model should be dropped). But it shouldn't throw away the authored free-end position of a dangling edge. I'd like some supported way to define/persist a model that contains dangling edges, so they survivetoJSON→initializeModel(which currently both strip) and can be reloaded.