Issues with loading async list for drop down in edit dialogue

Hi All

I am trying to make a node that has a config node and a drop down list of devices. The config node just contains the service, port and protocol of the server. The drop down list in the non-config node loads a list of devices from that server.

I had thought this would be a piece of cake as loads of nodes do similar things but I am having a few issues.

Issue number 1:
When I am initially setting up the first node and creating the config node I am unable to load the list of devices. The reason for this is when I make the async call from the edit dialogue to the node process the config node has not yet been saved so when I do

const configNode = module.nodes.getNode(req.params.configId)

in my node code that configID does not yet exist so nodejs does not have the server url to load nodes from.

In my editDialogue code I have:

            $('#node-input-server').change(() => {
                console.log(`CONFIG SERVER CHANGED: ${this.server}`);

                const newConfigNode = RED.nodes.node(this.server);

                console.log(newConfigNode);
            })

but this.server is undefined and there doesn't seem to be any way of getting the content of the config node to send it to the nodejs process to load the list of devices.

Issue number 2:
I have a validate function setup to ensure that a valid mac address is selected for the device:

        defaults: {
            name: {value:"soma-smart-shade"},
            server: {type:"soma-smart-shade-config", required: true},
            blindid: {validate: function(value) {
                const regExp = /^(\w{2}:){5}\w{2}$/;
                console.log(`VALIDATE: ${value} ${regExp.test(value)}`);
                return regExp.test(value);
            }}
        },

this works fine. The issue is that when I open the edit dialog the value of my select box is initially "Loading..." as the list of devices is loaded. This obviously validates as false but when the list of devices is loaded and I select the currently selected device in the list the validation function is not called again so it remains in an invalid state. I have to select a different option to get the validation function to run again.
Here is my code:

                $.getJSON(`soma-smart-shade-devices/${this.server}`, (data) => {
                    const blindIdInput = $('#node-input-blindid');
                    blindIdInput.prop('disabled', false);
                    blindIdInput.empty();

                    const defaultOption = new Option("Select blind...", "", true, true);
                    defaultOption.hidden = true;
                    defaultOption.disabled = true;

                    blindIdInput.append(defaultOption);

                    data.forEach(blind => {
                        blindIdInput.append(new Option(blind.name, blind.mac)); // , undefined, blind.mac === this.blindid
                    });
                });

how do I force a re-validation of the select value after creating the options?

Thanks

Hi @Roaders,
I think this discussion is about a rather similar issue. In my media node I get the config node (on the client side), but I send the config node properties to the server side endpoint (since I don't know whether it has been deployed yet):

configNode = RED.nodes.node(configNodeId);
             
// All client-side config data should be send to the server (since we don't know if it is dirty or not).
var configData = {};
configData.hostname = configNode.xaddress;
...
$.getJSON("onvifdevice/profiles/" + configNodeId, configData, function(profiles) {
   ...
}

Then in the endpoint on the server-side, I try to find the config node (and if I don't find it, I use the config node properties that the client side has passed):

var configNode = RED.nodes.getNode(req.params.config_node_id)

// Get the profiles of the camera, based on the config data on the client, instead of the config data
// stored inside this config node.  Reason is that the config data on the client might be 'dirty', i.e. changed
// by the user but not deployed yet on this config node.  But the client still needs to be able to get the profiles
// corresponding to that dirty config node.  That way the config screen can be filled with profiles already...
// But when the config data is not dirty, we will just use the profiles already loaded in this config node (which is faster).
// See https://discourse.nodered.org/t/initializing-config-screen-based-on-new-config-node/7327/10?u=bartbutenaers
configNode.getProfiles(req.query, res);
...

Note that my Onvif nodes are still in beta phase, so the logic might not be entirely correct yet!
Perhaps this is not exactly a solution for your problem, but it might give you an idea of how you can solve this ...

I don't know that... Hopefully somebody else can join the discussion.

Bart

It seems that the discussion about your issue comes to the same conclusion that I did. When I go to load the list of devices I should send all the data required to load the list from the config node as the data might have been changed or might not exist at all in node.js.
How do I get the new (or updated) config node properties though? As I mention above I tried this:

           $('#node-input-server').change(() => {
                console.log(`CONFIG SERVER CHANGED: ${this.server}`);

                const newConfigNode = RED.nodes.node(this.server);

                console.log(newConfigNode);
            })

but this.server is undefined so I can't get the new values.

Thanks for the reply.

OK, I managed to fix both my issues and I'll document them here in case anyone else has similar.

  • Issue One

as I suspected and as discussed elsewhere the issue with trying to load something based on a configuration node that might not exist or might be out of date on the server side means that when we try to load our list we need to send all the information required to do the load in the request from the edit dialog to our nodejs process.

I listen to changes in the config node here:

$('#node-input-server').change((event) => {
    this.loadBlinds();
})

link

and then I get the current value of the config node here:

this.loadBlinds = function(){
    const serverInput = $('#node-input-server')[0];
    const configNode = RED.nodes.node(serverInput.value);

    const devicesUrl = `soma-smart-shade-devices?protocol=${encodeURIComponent(configNode.protocol)}&host=${encodeURIComponent(configNode.host)}&port=${encodeURIComponent(configNode.port)}`;
    $.getJSON(devicesUrl, (data) => {
        // handle loaded data
    }
}

link

Doing this we have no reliance on the value of the node on the server side having been updated.

  • Issue Two

The other issues is just down to the fact that when we call val(myVal) on an html input the onChange event is not fired so the validation code does not rerun. This is fixed just by calling change():

const blindIdInput = $('#node-input-blindid');
blindIdInput.val(this.blindid);
blindIdInput.change();

link

That is really good to know. Thanks for sharing this tip with us!!