Using $.getJSON in oneditsave

When closing the config editor, I'd like to automatically set one of the available fields based on what the user has selected in another field i.e. if a particular device has been selected, then I want to set an appropriate productid. In oneditsave, I use $.getJSON to retrieve an object which defines these (device/productid) and other relationships. On the basis of some console.logs, it all seems successful - even setting the value of the productid html node. However the updated setting is not remembered; on next opening of the config editor, the product id entry is unchanged.

If (just as a test) I set the productid's value to something arbitrary outside of $.getJSON's function (commented out in code below), that value sticks.

Based on the above, it looks like the config editor is closing before $.getJSON has had time to do its thing. Is there some way to wait until for $.getJSON to finish?

I'm already using $.getJSON to retrieve the same data in oneditprepare. Could I save it somewhere to reuse later in oneditsave so I don't have to run $.getJSON in on editsave?

Code is:

oneditsave: function () {
        // Generate PID list for this device
        var device_pids = [];
        var device_selected = $("#node-input-device").val() || "ANY";
        $.getJSON('xkeys/products', function(xdata) {
            //console.log("XXXX " + device_selected + ", " + JSON.stringify(xdata));
            var selected_device = xdata[device_selected];
            if (selected_device) {
                console.log("YYYY " + selected_device.hidDevices[0][0]);
                device_pids.push(selected_device.hidDevices[0][0]);

                $("#node-input-productid").val(device_pids[0].toString());
                console.log("ZZZZ " + $("#node-input-productid").val());
                this.productid = $("#node-input-productid").val();
            } else {
                console.log("NO selected_device");
            }
        });
        //$("#node-input-productid").val("qwerty");
        //console.log("AAAA " + $("#node-input-productid").val());
}

BTW I'm using an array to hold productid numbers because a device can involve more than one of them. For simplicity, the code above only applies the first available productid

While waiting for advice on this, I tried saving the data in a hidden node in oneditprepare and was able to retrieve and use it successfully in oneditsave.

<div class="form-row">
    <input type="hidden" id="node-input-productstore">
</div>

Then in oneditprepare at the end of $.getJSON:

    $.getJSON('xkeys/products', function(product_data) {
        ....
        ....
        // Save the product data in a hidden node
        $("#node-input-productstore").val(JSON.stringify(product_data));
    }

Later:

oneditsave: function () {
    var product_data = JSON.parse($("#node-input-productstore").val());

    // Do something with product_data
    ....
    ....
}

That actually works quite well but I'm still keen to hear of better or more elegant solutions.

More to the point - although reusing saved data has worked in this case by removing the need to use $.getJSON, what about the more general case of calling any async function from within oneditsave? How should that be handled?

There is currently no support for async actions in oneditsave that need to complete before the node is updated - it is simply not a feature that's been needed or asked for up to this point.

If there is a real need for it, it wouldn't take much to add support for it. But would need to be done with care as it adds a bunch of new failure modes (timeout, rejected requests etc etc)

OK thanks, good to know.

Cant you just get values in oneditprepare and store them in a variable that is in scope for both oneditprepare and oneditsave instead of writing them to the DOM of a hidden element?

I do this with functions created in oneditprepare rather than save. Such that, if a user changes field1, I add a jQuery on-change function that listens for changes to that field and updates other fields accordingly.

This does let you go off down deep rabbit holes and I use various REST API's in uibuilder in this fashion.

No need to mess with oneditsave since everything is done dynamically.

Thanks Steve, I wondered about that but I'm still quite new to the Node-RED world and not really sure where to position a holding variable that would be in scope for both of them.

I did have a quick go at jQuery change function but it didn't immediately work. Now that I have something that does work, I can relax about it and maybe go back to it and work out why it didn't work. The field that changes is a typedInput (selection from a drop down with entries populated dynamically in oneditprepare) - not sure if that is a factor ...

Yes, that is probably the reason. You will want to use your browser dev tools to work out what is actually changing.

Did you use the deprecated .change(..) function or .on("change", ... ) ?

The latter should certainly work as per the docs - TypedInput Widget : Node-RED

Yes I used the old .change(..) function. Thanks for the correction - I'll try that.

EDIT: it worked straight away. It's also much nicer to see my Product ID field change as soon as the chosen Device changes.

Thanks again.