From e669fb349ac11fdca26b7271b557193c276166a7 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 6 Aug 2026 17:25:41 -0400 Subject: [PATCH] fix(adminui): #546 recycle DriverStatusPanel on Enabled flip via @key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DriverStatusPanel decides whether to subscribe exactly once, in OnInitializedAsync (`if (!Enabled) return;`), and exposes no OnParametersSet hook. The detail row was keyed only by DriverInstanceId, so a live config edit that flipped a row's Enabled true->false — surfaced on the page by the 5 s LoadConfigAsync refresh — reused the same panel instance: the markup correctly switched to "Disabled - not deployed" while the panel's two store SnapshotChanged subscriptions and its 5 s staleness timer stayed wired underneath, until the operator happened to collapse the row. Including the flag in the detail row's key recycles the component instead. Both directions verified against the panel's own lifecycle: true->false disposes the old instance (unsubscribing both stores, draining both timers) and mounts one that skips subscription setup entirely; false->true disposes an instance that never subscribed (the unsubscribes are no-ops, both timers null) and mounts one that subscribes and primes from the stores. The data row's key is unchanged. No behaviour change when Enabled is stable. Claude-Session: https://claude.ai/code/session_014WNM4vjoVksyyBraTXSZE1 --- .../Components/Pages/Hosts.razor | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Hosts.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Hosts.razor index ed5f652b..be387ea8 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Hosts.razor +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Hosts.razor @@ -189,7 +189,7 @@ else @foreach (var d in g.Drivers) { @* Keyed by instance id so the detail row's DriverStatusPanel is never - diffed onto a DIFFERENT driver: the panel resolves DriverInstanceId + diffed onto a DIFFERENT driver: the panel binds DriverInstanceId once in OnInitializedAsync (store subscription + resilience prime), so a reused instance would keep reporting the driver it first bound. *@ @@ -282,8 +282,17 @@ else { @* Full-width detail row beneath its driver. Rendered only while expanded so the table stays scannable and only the opened panels hold store - subscriptions — collapsing disposes the panel and unsubscribes. *@ - + subscriptions — collapsing disposes the panel and unsubscribes. + + Enabled participates in the key on purpose. The panel decides whether to + subscribe ONCE, in OnInitializedAsync (`if (!Enabled) return;`), and has no + OnParametersSet hook — so a live config edit that flips this row's Enabled + (surfaced by the 5 s LoadConfigAsync refresh) would re-render the panel's + "Disabled — not deployed" markup while its two store subscriptions and its + staleness timer stayed wired underneath. Keying on the flag recycles the + component instead: the old instance is disposed (unsubscribing both stores, + draining both timers) and a fresh one mounts against the new value. *@ +