Current Behavior
When a Dashboard ui-template is loaded/reloaded, Dashboard replays the last message stored for that widget. The replayed message appears identical to the original live message: it retains the original _msgid, payload and custom properties, with no metadata indicating that it came from the Dashboard datastore.
This is problematic when the authoritative state is maintained elsewhere in Node-RED. In my case, a mailbox widget receives count: 1 when mail arrives, while the authoritative current state is stored in global context. After the mailbox is cleared, the authoritative state can be count: 0, but a newly loaded Dashboard can initially replay an older count: 1 message.
I use ui-event / $pageview to fetch the authoritative state and send it to the ui-template:
ui-event ($pageview)
-> Fetch global state
-> ui-template
This means two independent messages can reach the widget during page loading: the Dashboard datastore replay and the current state fetched in response to $pageview.
I have observed the replay followed by the current state, but I cannot find a documented ordering guarantee. If the two mechanisms race, simply overwriting the replay with the $pageview result is not sufficient.
The workaround I now use is to ignore all messages until an explicit fetchGlobal message initializes the widget, and to attach a state timestamp/version to backend state so that an older replay arriving after initialization can also be rejected. This works, but requires application-specific synchronization logic solely because the template cannot identify a replayed message.
Checking _msgid alone does not help: normal live Node-RED messages also have _msgid, and the replay preserves the original value.
Exported ui-template nodes also contain properties such as:
"storeOutMessages": true,
"passthru": false,
"resendOnRefresh": true
resendOnRefresh sounds related, but I cannot find a corresponding option in the current ui-template editor, and it is unclear whether this property is currently implemented/used by Dashboard 2.
Expected Behavior
For applications where Node-RED owns the authoritative state, there should ideally be a supported way to distinguish datastore replay from a new live message.
For example, replayed messages could carry metadata such as:
msg._dashboard = {
replay: true
};
Alternatively, it would be useful to allow datastore replay to be disabled per widget, e.g. an option such as Restore last message on client load.
A documented guarantee that datastore replay always occurs before $pageview would also make explicit synchronization possible, although marking replayed messages or allowing replay to be disabled would seem more robust.
Steps To Reproduce
- Create a
ui-template that displays a value from msg.payload.count.
- Send a live message to it, for example:
msg.payload = {
count: 1,
date: Date.now()
};
msg.mailSource = "increase";
return msg;
- Observe the message in the template. For example:
{
payload: {
count: 1,
date: 1786717839872
},
topic: "",
_msgid: "4c284f268c64dd58",
mailSource: "increase"
}
- Maintain/change the authoritative application state separately (for example, set the corresponding global state to
count: 0).
- Add a
ui-event $pageview path that reads that authoritative state and sends count: 0 to the ui-template when the page loads.
- Reload/open the Dashboard page.
- Observe that the
ui-template also receives the previously stored count: 1 message. From inside the template it is indistinguishable from the original live message; the original _msgid and custom properties are preserved.
Environment
- Dashboard version: 1.30.2
- Node-RED version: 5
- Node.js version:
- npm version:
- Platform/OS:
- Browser:
Have you provided an initial effort estimate for this issue?
I am not a FlowFuse team member
Current Behavior
When a Dashboard
ui-templateis loaded/reloaded, Dashboard replays the last message stored for that widget. The replayed message appears identical to the original live message: it retains the original_msgid, payload and custom properties, with no metadata indicating that it came from the Dashboard datastore.This is problematic when the authoritative state is maintained elsewhere in Node-RED. In my case, a mailbox widget receives
count: 1when mail arrives, while the authoritative current state is stored in global context. After the mailbox is cleared, the authoritative state can becount: 0, but a newly loaded Dashboard can initially replay an oldercount: 1message.I use
ui-event/$pageviewto fetch the authoritative state and send it to theui-template:This means two independent messages can reach the widget during page loading: the Dashboard datastore replay and the current state fetched in response to
$pageview.I have observed the replay followed by the current state, but I cannot find a documented ordering guarantee. If the two mechanisms race, simply overwriting the replay with the
$pageviewresult is not sufficient.The workaround I now use is to ignore all messages until an explicit
fetchGlobalmessage initializes the widget, and to attach a state timestamp/version to backend state so that an older replay arriving after initialization can also be rejected. This works, but requires application-specific synchronization logic solely because the template cannot identify a replayed message.Checking
_msgidalone does not help: normal live Node-RED messages also have_msgid, and the replay preserves the original value.Exported
ui-templatenodes also contain properties such as:resendOnRefreshsounds related, but I cannot find a corresponding option in the currentui-templateeditor, and it is unclear whether this property is currently implemented/used by Dashboard 2.Expected Behavior
For applications where Node-RED owns the authoritative state, there should ideally be a supported way to distinguish datastore replay from a new live message.
For example, replayed messages could carry metadata such as:
Alternatively, it would be useful to allow datastore replay to be disabled per widget, e.g. an option such as
Restore last message on client load.A documented guarantee that datastore replay always occurs before
$pageviewwould also make explicit synchronization possible, although marking replayed messages or allowing replay to be disabled would seem more robust.Steps To Reproduce
ui-templatethat displays a value frommsg.payload.count.count: 0).ui-event$pageviewpath that reads that authoritative state and sendscount: 0to theui-templatewhen the page loads.ui-templatealso receives the previously storedcount: 1message. From inside the template it is indistinguishable from the original live message; the original_msgidand custom properties are preserved.Environment
Have you provided an initial effort estimate for this issue?
I am not a FlowFuse team member