How custom UI node recognize last value to display when switch tab on Node-Red dashboard?

Hi,

I have some problem about custom UI node recognize last value to display when switch tab in Node-RED dashboard.

First, I create custom UI node for display temperature. Now it works fine when it display on one tab.

After that, I create another tabs, then I test switch between tabs.

My custom UI node cannot recognize last value on it, until new value trigger again.

Please advise me,

Thank you in advance.

Dashboard has replyMessage system built in. Meaning that if you change tab or or do refresh the latest payload will be replied to the node. Known is that the reply can come in a bit too early. The widget is not yet ready in Dom so you can't show the incoming value. That situation can be covered by storing latest payload and use it when widget is truly ready to show it. Actual implementation depends a bit how the widget is built but nothing too complicated.

This is my main function. I am not sure, how to check widget is truly ready to show?

Please advise me.

function UiWidgetThermometer(config) {
        let node = this;
        let done = null;
        try {
            if (ui === undefined) {
                ui = RED.require("node-red-dashboard")(RED);
            }
            RED.nodes.createNode(this, config);

            // placing a "debugger;" in the code will cause the code to pause its execution in the web browser
            // this allows the user to inspect the variable values and see how the code is executing
            //debugger;

            if (checkConfig(node, config)) {
                const html = HTML(config);                    // *REQUIRED* get the HTML for this node using the function from above
                done = ui.addWidget({                       // *REQUIRED* add our widget to the ui dashboard using the following configuration
                    node: node,                             // *REQUIRED*
                    order: config.order,                    // *REQUIRED* placeholder for position in page
                    group: config.group,                    // *REQUIRED*
                    width: config.width,                    // *REQUIRED*
                    height: config.height,                  // *REQUIRED*
                    format: html,                           // *REQUIRED*
                    templateScope: "local",                 // *REQUIRED*
                    emitOnlyNewValues: false,               // *REQUIRED*
                    forwardInputMessages: false,            // *REQUIRED*
                    storeFrontEndInputAsState: false,       // *REQUIRED*
                    convertBack: function (value) {
                        return value;
                    },
                    convert: function(value) {
                        return value;
                        // console.log(value);
                        // var form = config.format.replace(/{{/g,"").replace(/}}/g,"").replace(/\s/g,"") || "_zzz_zzz_zzz_";
                        // form = form.split('|')[0];
                        // var value = RED.util.getMessageProperty(m,form);
                        // if (value !== undefined) {
                        //     if (!isNaN(parseFloat(value))) { value = parseFloat(value); }
                        //     return value;
                        // }
                        // if (!isNaN(parseFloat(p))) { p = parseFloat(p); }
                        // return p;

                        //return ui.toFloat.bind(this, config);
                    },
                    beforeEmit: function (msg) {
                        // Validate payload
                        const result = validatePayload(msg);
                        if (result.isError) {
                            msg.isErr = true;
                            msg.errMessage = result.message;
                            msg.percent = 0;
                        } else {
                            msg.percent = calculatePercentDisplay(msg, config);
                            msg.isErr = false;
                        }
                        // Bind 'unit' to msg
                        msg.unit = config.unit;
                        // Bind 'Number of decimals'
                        msg.numberOfDecimals = config.numberOfDecimals;
                        return {
                            msg: msg
                        };
                    },
                    beforeSend: function (msg, orig) {
                        if (orig) {
                            return orig.msg;
                        }
                    },
                    initController: function ($scope, events) {
                        console.log("events");
                        console.log(events);
                        $scope.flag = true;     // not sure if this is needed?
                        $scope.$watch('msg', function (msg) {
                            if (!msg) {
                                // Ignore undefined msg
                                return;
                            }
                            // Gathering payload
                            const payload = msg.payload;
                            const thermoWidget = document.getElementById("item_" + $scope.$eval('$id'));
                            const error = $(thermoWidget).find(".error");
                            $(error).hide();
                            // Validate payload
                            if (msg.isErr) {
                                $(error).show();
                                $(error).text("Error: " + msg.errMessage);
                            } else {
                                const mercury = $(thermoWidget).find(".mercury");
                                $(mercury).css("height", msg.percent.toString() + "%");
                                const tempDisplay = (Math.round(payload * 100) / 100).toFixed(parseInt(msg.numberOfDecimals));
                                $(mercury).children(".percent-current").text(tempDisplay.toString() + msg.unit);
                            }
                        });
                    }
                });
            }
        }
        catch (e) {
            // eslint-disable-next-line no-console
            console.warn(e);		// catch any errors that may occur and display them in the web browsers console
        }

        /**
         * REQUIRED
         * I'm not sure what this does, but it is needed.
         */
        node.on("close", function () {
            if (done) {
                done();
            }
        });
    }

I did a little search over node-red-contrib-ui ... nodes and found that this may be good example for you.

Just because of amount of code to read is quite small and it is quite easy to read.

Key points for you:
Line 37 -> ng-init='init(... ->
Line 107 $scope.init = function
Line 110 -> the actual check for existence. Init phase ends here.
Line 130 & 131 -> If not inited, storing the data to be used later.

You can find other kind of DOM existence checking methods like this

if ($('#elementId').length > 0) {
  // Exists.
}

They all do..

Advised to move out actual rendering part from the function $scope.$watch('msg', so you can call it from different places.

Thank you very much for your help. :+1:

Everything is done. This is solution for switching tab.

I just know, there is $scope.init and more things for fix solutions.

:blush:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.