What
#534 was fixed in the three ng-gauges (radial, linear, compass) by comparing widgetPathSignature() across effect runs and clearing the reading when it changes. The same defect is still live in every other widget that holds stream-derived presentation state, and the scaffolding still emits the vulnerable shape.
Why it happens
WidgetStreamsDirective rebuilds a subscription on a re-point, and DataService seeds each path with a BehaviorSubject, so the new path's value replays immediately — for most widgets that replay includes a leading null, which clears the display on its own. A widget that sets suppressBootstrapNull: true filters that null, so against a path that reports nothing the callback never runs at all and the previous path's reading stays on screen, presented as a live reading of the new path.
Scope
grep -rl streams.observe src/app/widgets matches 16 components; three carry the fix.
widget-gauge-steel still has the pre-fix shape, including the unconditional effectiveUnit.set('') the linear gauge replaced.
widget-simple-linear keeps dataValue / dataLabelValue / effectiveUnit across a re-point.
widget-numeric rolls its own subscription signature (including updateInterval, and without path normalization) and its own reset — a second convention for one concept.
tools/schematics/create-host2-widget/.../widget-__name@dasherize__.component.ts.template scaffolds if (pathCfg.path) { this.streams.observe(...) } with no clear, so a widget generated the documented way starts out with the bug.
Two other findings from the same review
- The reset sits in the wrong place.
WidgetStreamsDirective already computes the signature and already knows the moment it rebuilds a subscription (it stores {sub, signature} per path). A hook there would fix every consumer at once and remove the need to export widgetPathSignature at all. The current arrangement also depends on each widget passing a new closure to observe() on every effect run: the directive compares callback identity, so a stable reference — which the directive's own docs recommend — would make it early-return and leave the gauge blank on a live path until the next delta. widget-numeric already sits on that side.
- Zone bands are not covered by the widget-side clear.
WidgetMetadataDirective.observe() returns early on a falsy path without resetting _meta, and the gauges' highlights computed gates on zones/cfg/theme but never on whether a path is configured. Clearing a widget's path therefore leaves the previous path's warn/alarm bands painted around a gauge showing --. Verified against the real directive: zones().length stayed at 2 after applyMetaConfigDiff with path: null and with path: ''. widget-metadata.directive.ts has no spec file at all.
Suggested order
Move the reset into WidgetStreamsDirective, migrate the three gauges and widget-numeric onto it, fix the schematic template, then handle the metadata directive's cleared-path case.
What
#534 was fixed in the three ng-gauges (radial, linear, compass) by comparing
widgetPathSignature()across effect runs and clearing the reading when it changes. The same defect is still live in every other widget that holds stream-derived presentation state, and the scaffolding still emits the vulnerable shape.Why it happens
WidgetStreamsDirectiverebuilds a subscription on a re-point, andDataServiceseeds each path with aBehaviorSubject, so the new path's value replays immediately — for most widgets that replay includes a leadingnull, which clears the display on its own. A widget that setssuppressBootstrapNull: truefilters that null, so against a path that reports nothing the callback never runs at all and the previous path's reading stays on screen, presented as a live reading of the new path.Scope
grep -rl streams.observe src/app/widgetsmatches 16 components; three carry the fix.widget-gauge-steelstill has the pre-fix shape, including the unconditionaleffectiveUnit.set('')the linear gauge replaced.widget-simple-linearkeepsdataValue/dataLabelValue/effectiveUnitacross a re-point.widget-numericrolls its own subscription signature (includingupdateInterval, and without path normalization) and its own reset — a second convention for one concept.tools/schematics/create-host2-widget/.../widget-__name@dasherize__.component.ts.templatescaffoldsif (pathCfg.path) { this.streams.observe(...) }with no clear, so a widget generated the documented way starts out with the bug.Two other findings from the same review
WidgetStreamsDirectivealready computes the signature and already knows the moment it rebuilds a subscription (it stores{sub, signature}per path). A hook there would fix every consumer at once and remove the need to exportwidgetPathSignatureat all. The current arrangement also depends on each widget passing a new closure toobserve()on every effect run: the directive compares callback identity, so a stable reference — which the directive's own docs recommend — would make it early-return and leave the gauge blank on a live path until the next delta.widget-numericalready sits on that side.WidgetMetadataDirective.observe()returns early on a falsy path without resetting_meta, and the gauges'highlightscomputed gates on zones/cfg/theme but never on whether a path is configured. Clearing a widget's path therefore leaves the previous path's warn/alarm bands painted around a gauge showing--. Verified against the real directive:zones().lengthstayed at 2 afterapplyMetaConfigDiffwithpath: nulland withpath: ''.widget-metadata.directive.tshas no spec file at all.Suggested order
Move the reset into
WidgetStreamsDirective, migrate the three gauges andwidget-numericonto it, fix the schematic template, then handle the metadata directive's cleared-path case.