VueJs - where to store my data

Hi folks,

Whenever I have an hour free, I try to start migrating my old ui nodes to D2. But due to my lack of Vue knowledge, I find it rather difficult to get started. Seems that Vue is doing a lot of magic behind the scenes, but that makes it even more difficult for me to get a hold of it.

I have lost completely track about where all my data needs to be stored. I read this documentation, but it is still foggy in my head.

For example:

  1. The {{ props.label }} in my template is binded to my props.props. So the props contain (immutable?) data arriving from the config screen of my ui node. Personally I find it rather confusing to have 2 nested props levels, especially since the upper props level seems nowhere to be used. I assume that is part of the Vue magic...

  2. Then there is props.state. I thought first that this is some volatile state of my widget, which gets lost (after a refresh?). But I seem to remember that I read somewhere that state on the server side is also used to store the dynamic settings arriving via input messages (which overrule the corresponding settings from my ui node's config screen)? But perhaps I am mistaken. So not sure whether I can store here some of my custom state, without it being overwritten by the dashboard?

  3. Then there is the data section. Not sure for which data this is used.

  4. There are also Vuex datastores. Is that for persistent data, or is it the same as the above props in some way? Because it also seems to be used to store state...

  5. In the above example the props.label is showed in the widget. While I understand that is useful for fixed texts, in my use case I need to be able to change the label afterwards. Does this mean I need to copy in js code my props.label content to somewhere else, and then somehow bind that intermediate variable to my widget? Or perhaps that needs to go into the computed data section?

  6. Then there is the data being stored in the input messages, and those messages are also being stored somewhere. Not sure anymore if there is a msg replay mechanism (e.g. at browser refresh) like in the old days?

  7. It is also not clear to me how and when the data is transported between the server side and the client side. For example when I save data to the server-side datastore, what happens then? Or when a client connects, how it loads his data from my server. While some developers might not need this kind of information, it would help me to find a good mechanism to store and share my state in the SVG node...

I had hoped my cheat sheet would gain me some insights in how it is all glued together, but my sheet is lacking too much information to be of any help unfortunately :frowning_face:

I have similar issues with my migrated heatmap node. It looses all his data when the browser window has refreshed. No idea how to solve that to be honest. Should have more time to read about Vue and experiment with it, but that is completely no option for me at the moment.

Thanks for illuminating me!!

Bart

Just to put a quick note to say that some of these are Vue things, some are my design decisions, some of it a little legacy that I need to clean out (props.state I dont think is used anymore, but need to double check)

I will give you a full descriptive answer to each of your points tomorrow!

1 Like

I you need to prepare things for your webinar, please let this wait for a couple of days!!

For anybody interested in this topic. I have been looking this evening for some visual explanation of how it works, but I assume I am using the wrong keywords because couldn't find much. Which would be weird.

Did some quick drawing, and here is what I found so far:

Noob level stuff, but need to start somewhere...

2 Likes

Now you're damaging my self esteem :face_with_raised_eyebrow:

2 Likes

1. props

  1. The {{ props.label }} in my template is binded to my props.props. So the props contain (immutable?) data arriving from the config screen of my ui node. Personally I find it rather confusing to have 2 nested props levels, especially since the upper props level seems nowhere to be used. I assume that is part of the Vue magic...

So, this is perhaps bad attribute naming on my part, but the breakdown is two-fold:

props: {
    ....
}

On the component definition is a VueJS Options API. props is a schema to tell anything else in our Vue app what inputs/properties this component needs in order to render. So, for example <my-component prop1="" prop2="" />

Our widget specification is driven by three properties: id, props and state.

id: The ID given to the node/widget from Node-RED
props: The object sent by ui-base as part of the ui-config, which bundles together the appropriate values from the Node-RED configuration for that node, perhaps config would have been a better choice of variable names, rather than the double props, this is something I'm definitely open to changing for easier readability.
state: This is meant to be providing details on anything overridden at runtime, but I haven't implemented this consistently, entirely my fault.

2. props.state

  1. Then there is props.state. I thought first that this is some volatile state of my widget, which gets lost (after a refresh?). But I seem to remember that I read somewhere that state on the server side is also used to store the dynamic settings arriving via input messages (which overrule the corresponding settings from my ui node's config screen)? But perhaps I am mistaken. So not sure whether I can store here some of my custom state, without it being overwritten by the dashboard?

My original plan for props.state was to store dynamic content in here, i.e. visibility, disabled state, etc. but the lines between props and state were not clear, as often you can override the props at runtime, e.g. options on a ui-dropdown. The difficulty then came (which I can see you're facing in 5 is that I was duplicating effort client-side of having to constantly check both props and state, and it's messy.

As a result, it's become a bit of a mish-mash of values that I really need to clean up and provide consistency on, but with all the new requests, bugs, etc. coming in, and now only working on Dashboard 2 days a week at most, spending a few days just doing technical debt, and ensuring cleaner documentation hasn't been possible.

Server-side, my architecture is a little cleaner, where I also use a store-based architecture, with data matching the behaviour of the client, i.e. storing message history, and the statestore (server-side) storing anything that is overridden from the base configuration, so Node-RED maintains the configuration defined at deploy-time, and then statestore keeps track of anything that has since changed. The two stores are then merged

3. data () { ... }

  1. Then there is the data section. Not sure for which data this is used.

This is a part of the VueJS Options API to define a Vue Component. data () is entirely scoped locally to the Vue component, and defines variables that are used component-wide, i.e. rendered part of the HTML and/or used across JavaScript methods defined on the component too.

4. VueX Stores

  1. There are also Vuex datastores. Is that for persistent data, or is it the same as the above props in some way? Because it also seems to be used to store state...

vuex is used to persist data across the application in a centralised location, any component can read/write into the stores we have. Within Dashboard 2.0, I have the following stores:

  • data: this stores any of the latest or historical messages for each widget/node
  • ui: this stores the ui-config for the whole Dashboard as sent by Node-RED, including th full list of pages, groups, themes and widgets.
  • setup: This is shoe-horned a little, but fundamentally stores the setup object sent on first load, which in core provides the SocketIO configuration, generated server-side. It is also possible to extend this with the Third Party Plugins (and is what we use for the Multi-User Dashboards)

5. Dynamic Properties

  1. In the above example the props.label is showed in the widget. While I understand that is useful for fixed texts, in my use case I need to be able to change the label afterwards. Does this mean I need to copy in js code my props.label content to somewhere else, and then somehow bind that intermediate variable to my widget? Or perhaps that needs to go into the computed data section?

So, the "cleanest" (I use this phrase in the loosest of terms) example I have for this is ui-dropdown. When a message is received with .options inside, server-side we have a beforeSend() handler, which is before it's sent to the client. This stores, in our server-side statestore any msg.options that are found.

Client-side, we then also check for .options when a new message is received in the custom onInput function, and store that locally on our component's this.items.

On refresh, our ui-base, when building the ui-config merges the configuration from Node-RED, which would still have the original options and the statestore into the widget configuration, so the latest .options would be provided to the client-side where they would be loaded and shown.

6. OnLoad

  1. Then there is the data being stored in the input messages, and those messages are also being stored somewhere. Not sure anymore if there is a msg replay mechanism (e.g. at browser refresh) like in the old days?

Each core widget can provide an onLoad function. When our client first loads a widget, the server-side will pass an on-load event via SocketIO with the latest msg it's aware of. So, if you want to load the latest state, provide a listener for onLoad client-side (for third party widgets - docs are here) which accepts that msg and does whatever you need to do with it on the client.

7. Data Transfer

  1. It is also not clear to me how and when the data is transported between the server side and the client side. For example when I save data to the server-side datastore, what happens then?

Data being stored to the statestore just does exactly that, nothing is communicated to the client unless you explicitly request it to be. Our Events Architecture diagram does cover the foundatinal event structure between Client (in blue) and Node-RED (in red).

Or when a client connects, how it loads his data from my server. While some developers might not need this kind of information, it would help me to find a good mechanism to store and share my state in the SVG node...

In this particular example, you see in the diagram linked above that we fire a widget-load event for each widget on the screen. The server-side nodes then send a widget-load event back to the client with the latest msg stored in the server-side datastore

Hi @joepavitt,
Thanks for taking the time for giving so much feedback in depth!
And all respect that things can grow out of hand when you have to develop something big like this from scratch in very short amount of time.

I am going to try to find some time in the next evenings to digest this info, and hopefully I can map all of it on my cheat sheet. The amount of missing arrows visualize the gaps in my knowledge :wink:

Good luck with your webinar!!

I will also make sure i have some explicit examples added into the docs for your use case around data loading.

Fundamentally there are two options:

  1. Utilise the in-built mechanisms if you only need the last message received, or if you want all messages received and stored.
  2. Build your own with our datastores, that stores your own custom format - suspect you want this, and so I will document an example.
1 Like

Issue to track doc changes: Docs: Add better/updated documentation of the events hierarchy of Dashboard 2.0 · Issue #646 · FlowFuse/node-red-dashboard · GitHub

PR opened with improved diagrams and documentation: Docs: Update Events & State Store Architecture Docs & Diagrams by joepavitt · Pull Request #647 · FlowFuse/node-red-dashboard · GitHub

Hi @joepavitt,

I read through the readme files in your pull request, and that is much more clear. Thanks for all your efforts!! Really appreciated...

I am going to print your 3 separate diagrams on a large piece of paper, and put them beside me when I develop/migrate my ui nodes. I am pretty sure it will solve a lot of my headaches. When something is not clear, I will get back to you.

Personally I would find that much more readable. Because it is the config from the node's config screen in Node-RED, which arrives via ui-config in the dashboard. So if it would arrive at the end of the chain in props.config, that would make more sense to me.

Do I understand this correctly: if you need the data only inside your component (i.e. ui node) then you can put it in the data. And if you need the data across components, then you need to put it into a store? If correct, it might be useful for beginner developers to put that in the documentation. I didn't find it at first sight, but perhaps it is already there...

Although it is quite a lot to digest after a hard day of work (in other technologies), but you have provided all the bits and pieces that I needed. Now it is up to me and the other ui node developers to start puzzling. But gradually the fogs starts clearing in my mind...

Thanks again!!!!!!

1 Like