How to change the name of a config field without breaking old flows

In a contrib node I need to change the name of a field. Can anyone suggest how I go about doing that such that config from old nodes is updated to use the new field name automatically? It is ok if the user has to open each old one and save it again, but I don't want him to lose the existing field contents.

It is a dashboard 2 ui node (gauge classic) and I should have called the class field className so that the dashboard automatically picks it up.

Hi Colin,

I should be saying use semantic versioning :wink:

But one way, is to check for both variants in the code behind.


const foo = config.newProp || config.oldProp

In the defaults section, keep them both, BUT only create an element for the new? But don’t make it required for a while

And when the config editor is opened
(Will need to check if it’s already been set)

this.newProp = this.oldProp

I don’t develop for dashboard, so I maybe incorrect in the complexity

I do, but I want to avoid a breaking change if possible, and if not then to at least minimise the impact.

Unfortunately there is no such code. Provided I get the field name right (className) then it is all handled by the core D2 code (server side at least).

I thought of doing something like that, but, unfortunately, if I import an old config then it seems that (in onEditPrepare) this.oldProp is undefined. I guess because the field does not exist in the html file. Perhaps I need to keep the old field as a hidden field.

oldProp should only be undefined if you've allowed that as a valid thing in your previous version. And in that case, it shouldn't matter if you pass undefined to the new prop.

oldProp is the name of the field in the old config. If the node is updated to the new version then, preferably, it should pick up the oldProp and move it to the new one. Otherwise the old value will be lost, but in the config passed in to onEditPrepare the oldProp is undefined even if it has a value in the old config.

Edit I am testing this by importing a node from an instance of node-red running the old software, then opening the node in the new s/w. I presume that is a valid way of doing it.

Because oldProp should always be defined in node.defaults to access to the value. Just remove the value property.

Ah, I missed out 1 thing of course - when you need to change variables like this, you need to keep the oldProp variable. You will need to leave that code in your html file until you can be certainly nobody has the old version any more and that everyone with the new version has refreshed their settings. Basically, you will find it hard to remove the old variable. Generally though, it doesn't really matter.

So rather than moving oldProp to newProp, just make sure that any oldProp value is copied to newProp if newProp does not contain a value.

Did you mean, in defaults, where previously I had
oldProp: {value: ""}
to change that to
oldProp: {}

I tried that and now oldProp does appear in the config keys, but it is still undefined when I import an old config and open the node. Do I also have to keep the html for it, but make it a hidden field?

No, but you do need to keep it in defaults in the html file AND in the node definition in your runtime js.

I am not sure exactly which code you are referring to.

In the runtime:

/** 2) This is run when an actual instance of our node is committed to a flow
 * type {function(this:runtimeNode&senderNode, runtimeNodeConfig & senderNode):void}
 * @param {runtimeNodeConfig & tiDummyNode} config The Node-RED node instance config object
 * @this {runtimeNode & tiDummyNode}
 */
function nodeInstance(config) {
    // As a module-level named function, it will inherit `mod` and other module-level variables

    // If you need it - which you will here - or just use mod.RED if you prefer:
    const RED = mod.RED
    if (RED === null) return

    // @ts-ignore Create the node instance - `this` can only be referenced AFTER here
    RED.nodes.createNode(this, config)

    /** Transfer config items from the Editor panel to the runtime */
    this.name = config.name ?? ''
    this.topic = config.topic ?? ''

    /** Handle incoming msg's - note that the handler fn inherits `this` */
    this.on('input', inputMsgHandler)
}

The config parameter is what contains all of the definitions passed from your html file. You create a node instance from the config. Oddly, you have to then manually transfer the config to your node instance. Never been quite sure why RED.nodes.createNode(this, config) doesn't do that automatically. Most people assign const node = this and then use node but that isn't necessary really, I just use this.

this or node contains the data defined by the flow author when they deploy an instance of your node but it is the runtime code that actually maintains the data. If you miss out that assignment step, your node instances don't save any of the settings.

I don't have that, and if you look at the basic example in the docs then it doesn't either - Creating your first node : Node-RED

Interesting, always something to learn! Just did a quick test. So I guess it does save - however, if you don't transfer the config to the node instance, you won't really be able to easily use it in the runtime. Most examples do do this. I guess I made an assumption.

You might need to share some more code then to try and find the issue.

I have just realised why I am confused. I have only been working with D2 ui nodes recently. Looking back at my non-ui nodes I see that I am doing exactly what you say. The difference with the ui nodes is that the server I haven't needed to access the config directly, it is pretty much all in the client side.

Back on the fundamental problem, I have realised that it probably isn't possible to rename the field without at least forcing the user to open and close all existing nodes following the update. I have also realised that I can keep the existing name and work around the issue with a really trivial change in the code, so I have done that instead.

Thanks to all who offered help.

2 Likes

For some odd reason, I find that I can't find a reason to work on D2 nodes. :grin:

Joking aside, it's a shame that you don't. Uibuilder is a fantastic bit of work and if it seamlessly integrated with db2 and the 2 bodies of work offered users enhanced features it would be win win for the community.

I have absolutely no idea what that looks like, entails or if is even possible as I have such little time these days to explore this. Sure I know you could use ifrsmes or link from one to the other, but I'm thinking tighter integration, reusable components?, providing "pages" that are available as a tab? More?

Honestly, it is more the other way around Steve. I've offered repeatedly to collaborate not only before D2 was announced but previously with flexdash as well. UIBUILDER has been around a lot longer than D2.

I've also previously tried to engage on standardising message schema's and you will note that I went out of my way recently to add the msg,_client schema to UIBUILDER v7.

Also, being totally honest, the recent and rather inaccurate blog post about D2 vs "ui-builder" (sic) didn't really make me feel all that welcome.

As someone who has been active in supporting Node-RED for over 10 years, I've ALWAYS tried to encourage joined up work as I know fully the value of it.

I remain open to any ideas people have about a more joined-up approach between UIBUILDER and D2.

I'm sorry to hear that Julian. The article was written based on your original forum post, and we even included the table you made in the blog post.

If there are inaccuracies, please do surface those (via DM, here, or even better open an issue on our website repo and we will get those corrected)

I have been very grateful of your collaboration (e.g. msg._client was an excellent move for both libraries), and we are keen to continue that moving forward, e.g. FF explicitly investing in development time just so that UI Builder runs on FF Cloud.

1 Like

Thanks Joe, I will DM you when I get a chance to respond properly. Though I would point out that if someone searches for "node-red-contrib-ui-builder", they won't find anything since that is not the correct package name.

Now now kids!

You're missing a trick! - a thing called Flash and Action Script? :face_with_hand_over_mouth:

(to add some humour :smile: )

-- ENDOFSPAM