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.