One of the things that keeps nagging me in the implementation of FlexDash is the tracking of dependencies between nodes. The std dashboard has exactly the same issue and I'm running into this elsewhere too. I fear there is no good answer right now, but I'm wondering whether there's any energy to try and find a clean solution.
In FlexDash, widgets depend on the container (e.g. panel, grid, tab) in which they're contained. The dependency itself is handled by Node-RED in that a node can depend on a config node. However, FlexDash also needs to track the order of dependents because that's the order in which they get placed on the screen. This tracking of dependency meta-data is not supported by Node-RED.
The std dashboard has the exact same issue. It encodes the order in the dependent as an index. I tried to understand the code that maintains those indexes and failed. FlexDash instead encodes the order as an array of node IDs in the container. It's not pretty code either.
The issues that I believe both dashboards run into have to do with deleting nodes (disabling nodes further muddies the water). In addition, there are issues around which nodes get affected by a change and around flow editor vs. runtime.
Focusing on the flow editor, some of the primitives I was missing when I implemented the FlexDash code:
- given a NodeID how to determine its state: active, disabled, deleted-but-undoable, deleted/non-existent
- getting a callback when a node transitions between the above states, or alternatively when the flow gets externalized (save/export)
- possibly getting a callback when all flows/nodes have been loaded so one can determine the state of all nodes to which one holds a reference
Let me try an example to illustrate the challenges:
- suppose you have nodes A, B, C, D in that order in container X and the user deletes node C
- the node ordering should now be changed to A, B, D
- the first question is when and how does the relevant code get triggered?
- the second question is what are the side-effects?
- in FlexDash the deletion changes node X where the array of dependents lives and thus dirties X
- in ui_base the index of D would have to be changed and thus dirties D
- both cause undesired effects on seemingly unrelated nodes that suddenly appear changed in the editor and need to be deployed
In terms of editor/runtime split, the above issue causes double fun: if C cannot be cleanly removed in the editor before deploy then when the runtime gets the flow the ordering is inconsistent/denormalized and the runtime code has to implement its own work-around. In the case of FlexDash where the config can also be edited live in the dashboard the issue propagates there too. All this can be worked around but it creates opportunities for fragile and inconsistent code. It would be much better if one could guarantee in the editor that what the runtime sees is normalized.
As I'm writing this, I'm seeing some solutions where I didn't pick the optimal option when I implemented the bulk of node-red-flexdash a couple of months back. If I remove/disable all live editing in FlexDash and if I had the following two primitives I could clean up a lot of code:
- RED.node.getState(node_id) -> active | disabled | deleted (dunno how undo factors in here)
- a way to register a callback for a node that gets invoked before the node gets externalized so it can modify the config, i.e. callback(config) -> config_to_output; this can normalize the config for the run-time (I believe I've seen code that does exactly this for internal purposes)
- a way to get a callback when any node is deleted (again, dunno how undo factors into this)
What these primitives would enable is:
- know when the ordering of dependents in the current config can be safely changed to remove deleted nodes
- normalize the config for the run-time and remove code from the run-time to deal with seemingly missing nodes
NB: it would also be great to have a clear description of how undo works 'cause it does factor in here and I'm pretty sure the FlexDash nodes are not doing the right thing currently.