node-red-dashboard icon indicating copy to clipboard operation
node-red-dashboard copied to clipboard

Secondary Tab Group(s) with ui-dropdown do not populate msg.ui_update.options if they are not the active/visible group

Open nikolaioak opened this issue 8 months ago • 4 comments

Current Behavior

When I have a dashboard using the Tab Layout, I am dynamically populating various ui-dropdown nodes with options across multiple groups. The ui-dropdown node within the active tab are populated with the options when the page is viewed using a ui-event node. I can confirm that messages are passed to each ui-dropdown using debug nodes, but no options are populated.

Expected Behavior

I would expect all ui-dropdowns on separate tab groups to populate with options, so that when a separate tab group is accessed, the options are available.

Steps To Reproduce

Configure a page with tab layout, add multiple tab groups, and place a ui-dropdown node on each group. Dynamically provide options to each ui-dropdown node using msg.ui_update.options, originating from a ui-event node.

Open the dashboard page with tab groups, navigate to a secondary group, and confirm that no options are populated.

Sample flow code: [ { "id": "e8e12134c6e25677", "type": "ui-dropdown", "z": "44d110eda5c2622b", "group": "4cf27e96a0500a25", "name": "Drop-Down 1", "label": "Select Option:", "tooltip": "", "order": 1, "width": 0, "height": 0, "passthru": false, "multiple": false, "chips": false, "clearable": false, "options": [ { "label": "", "value": "", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "typeIsComboBox": true, "msgTrigger": "onChange", "x": 860, "y": 840, "wires": [ [] ] }, { "id": "7042d7873f6cd7e3", "type": "ui-event", "z": "44d110eda5c2622b", "ui": "400f2675d972ee11", "name": "", "x": 240, "y": 880, "wires": [ [ "c43b94542a49c22a" ] ] }, { "id": "c43b94542a49c22a", "type": "switch", "z": "44d110eda5c2622b", "name": "Tab Test Page?", "property": "payload.page.name = \"Tab Test\" and topic = \"$pageview\"", "propertyType": "jsonata", "rules": [ { "t": "true" } ], "checkall": "true", "repair": false, "outputs": 1, "x": 400, "y": 880, "wires": [ [ "98b343e3db216ec0" ] ] }, { "id": "98b343e3db216ec0", "type": "change", "z": "44d110eda5c2622b", "name": "", "rules": [ { "t": "set", "p": "ui_update.options", "pt": "msg", "to": "[\"A\",\"B\",\"C\"]", "tot": "json" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 620, "y": 880, "wires": [ [ "e8e12134c6e25677", "885caa0eea15d991", "979d0d4950d09bdc" ] ] }, { "id": "885caa0eea15d991", "type": "ui-dropdown", "z": "44d110eda5c2622b", "group": "3c9bfa7efc2644f1", "name": "Drop-Down 2", "label": "Select Option:", "tooltip": "", "order": 1, "width": 0, "height": 0, "passthru": false, "multiple": false, "chips": false, "clearable": false, "options": [ { "label": "", "value": "", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "typeIsComboBox": true, "msgTrigger": "onChange", "x": 860, "y": 880, "wires": [ [] ] }, { "id": "979d0d4950d09bdc", "type": "ui-dropdown", "z": "44d110eda5c2622b", "group": "d4ccab1b08ad44e9", "name": "Drop-Down 3", "label": "Select Option:", "tooltip": "", "order": 1, "width": 0, "height": 0, "passthru": false, "multiple": false, "chips": false, "clearable": false, "options": [ { "label": "", "value": "", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "typeIsComboBox": true, "msgTrigger": "onChange", "x": 860, "y": 920, "wires": [ [] ] }, { "id": "4cf27e96a0500a25", "type": "ui-group", "name": "Tab 1", "page": "2f89576d1fcf7164", "width": "6", "height": "1", "order": 1, "showTitle": true, "className": "", "visible": "true", "disabled": "false", "groupType": "default" }, { "id": "400f2675d972ee11", "type": "ui-base", "name": "Dashboard", "path": "/dashboard", "includeClientData": true, "acceptsClientConfig": [ "ui-notification", "ui-control", "ui-template", "ui-button", "ui-text-input", "ui-radio-group", "ui-markdown", "ui-audio", "ui-gauge", "ui-chart", "ui-table", "ui-text", "ui-switch", "ui-slider", "ui-button-group", "ui-file-input", "ui-number-input", "ui-form", "ui-dropdown" ], "showPathInSidebar": false, "navigationStyle": "default", "titleBarStyle": "default" }, { "id": "3c9bfa7efc2644f1", "type": "ui-group", "name": "Tab 2", "page": "2f89576d1fcf7164", "width": "6", "height": "1", "order": 2, "showTitle": true, "className": "", "visible": "true", "disabled": "false", "groupType": "default" }, { "id": "d4ccab1b08ad44e9", "type": "ui-group", "name": "Tab 3", "page": "2f89576d1fcf7164", "width": "6", "height": "1", "order": 3, "showTitle": true, "className": "", "visible": "true", "disabled": "false", "groupType": "default" }, { "id": "2f89576d1fcf7164", "type": "ui-page", "name": "Tab Test", "ui": "400f2675d972ee11", "path": "/test", "icon": "home", "layout": "tabs", "theme": "6aed771fda3270a1", "breakpoints": [ { "name": "Default", "px": "0", "cols": "3" }, { "name": "Tablet", "px": "576", "cols": "6" }, { "name": "Small Desktop", "px": "768", "cols": "9" }, { "name": "Desktop", "px": "1024", "cols": "12" } ], "order": 2, "className": "", "visible": "true", "disabled": "false" }, { "id": "6aed771fda3270a1", "type": "ui-theme", "name": "Global Theme", "colors": { "surface": "#212121", "primary": "#2aace2", "bgPage": "#121212", "groupBg": "#212121", "groupOutline": "#585858" }, "sizes": { "pagePadding": "12px", "groupGap": "12px", "groupBorderRadius": "4px", "widgetGap": "12px", "density": "default" } } ]

Environment

  • Dashboard version: v1.22.1
  • Node-RED version: 3.1.15
  • Node.js version: 18.20.5
  • npm version: 10.8.2
  • Platform/OS: FlowFuse v2.14.1, Node-RED Launcher v2.14.1-3.1.x on Kubernetes/Linux AMD x86_64
  • Browser: Confirmed on both Firefox Version 137.0 (ARM 64-bit) as well as Chrome Version 135.0.7049.42 (Official Build) (arm64)

Have you provided an initial effort estimate for this issue?

I am not a FlowFuse team member

nikolaioak avatar Apr 10 '25 15:04 nikolaioak

This bug was reported by https://app-eu1.hubspot.com/contacts/26586079/record/0-1/26913651

robmarcer avatar Apr 17 '25 13:04 robmarcer

@joepavitt could you comment when you have some time please?

robmarcer avatar Apr 17 '25 13:04 robmarcer

Thanks Rob, I'm out until Friday next week, but @Steve-Mcl could take a look before that

joepavitt avatar Apr 17 '25 13:04 joepavitt

@Steve-Mcl can you investigate this please? I think there may be a fundamental architectural gap here with our rendering of the widgets when switching tabs, they should be retrieving the server-side state on mounted. That server-side state should have the latest options in it.

It's likely to be one of:

  • It's not reading from server-side when mounted
  • The event handler for the dynamic update is updating the server side state, but not the client side because it's not running the active event listeners. If it's mounted, it should be doing this. If it's not mounted, it should be getting server-side state when it is mounted anyway.
  • The server-side state is not getting updated at all, but this is unlikely as options persists on a refresh

joepavitt avatar Apr 25 '25 09:04 joepavitt

We have had a request for status from the customer on this item - would be good to get it looked at as a priority next week.

knolleary avatar May 08 '25 14:05 knolleary

@Steve-Mcl This one passed 2.17, but let's keep moving forward on this one.

gstout52 avatar May 09 '25 20:05 gstout52

Just worked through this with @Steve-Mcl - it's not straight forward, but I will do my best to articulate:

  • The issue arises from when and how we store "state" on a widget. When you send a ui_update request, we (a) send it to the client, where any actively rendred widgets receive it and action it, and (b) store the state server-side in our "state store". When a widget then first rendered in the UI, we have a widget-load event which reads the "state" from the store, and displays the updated options. This works correctly.
  • However, we only do this for widgets that have a shared state across all client connections. If the "UI Dropdown" is toggled on in the "Client Constraints" tab for Dashboard (such that each dropdown has unique state per user), then this message is only sent to the front-end connection specified in msg._client (which UI Event includes), and is not stored server-side. This means that the client-side widgets not yet rendered, e.g. dropdowns on other tabs, do not receive the request to update their options, nor is it stored server-side.

Workarounds:

  • Make the options universal across all client connections, you can do this in one of two ways:
    1. Un-toggle ui-dropdown from the "Client Constraints" tab in the Dashboard sidebar. This will mean that all dropdowns share state across all client/user connections.
    2. Add a change node in after the ui-event node, and Delete the msg._client object. This will mean that only this property and dropdown will share state across all client connections
  • We introduce a new $tabchange event that is emitted by UI Event. This will fire the request to dynamically populate the options (and the same for any other widget in question) which gives the coverage needed.

The latter workaround is imo the correct fix here as it provides coverage across all of the user cases

joepavitt avatar May 28 '25 11:05 joepavitt

I have set this to "backlog" in dashboard tracker and to "todo" in flowfuse tracker.

Steve-Mcl avatar May 28 '25 11:05 Steve-Mcl

Thanks for the follow up! We do require user context for the drop-downs in other spots so I’ve updated the flow to remove msg._client when the drop-downs are populated. The only detriment here is that if user 2 loads the page after user 1 has selected some of the items in the drop down, user 1’s drop-down selection is cleared. I don’t have a lot of items in these particular drop-downs and this scenario might be rare, but could cause some confusion. Should be okay for now.

Regardless I agree the best way to handle this would be on the tab change event, so it’s good to see that this feature is being worked on.

nikolaioak avatar May 28 '25 14:05 nikolaioak

This would be valuable to https://app-eu1.hubspot.com/contacts/26586079/record/0-1/26913651

robmarcer avatar Jun 05 '25 14:06 robmarcer