Message sent once but received twice

I'm using ui-template nodes in my Node-red dashboards, which receive messages from other nodes.
my message handling is as follows:

<script type="text/javascript">
var $scope = this.scope;
(function(scope) {
    $scope.$watch('msg', function(msg)
    {
        if (msg)
            console.log(`Incoming msg: payload=${msg.payload||"None"}`);
            // Process the msg
    });
})(scope);
</script>

When I load the page (dashboard tab) via a direct URL (using .../ui/#!/<n>), everything works OK. However, if I move to this page from another dashboard tab (using the dashboard's hamburger menu), the last msg is automatically sent again into the template node.
Consider the following flow:
image
I injected a timestamp to the template node, then moved to another tab and came back. The previous timestamp message was resent automatically:
image
Note that I have unchecked the "Reload last value on refresh" in the template node
image

Am I doing something wrong? If it's a bug, I do not expect Node-red team (@joepavitt ) to fix it as they are moving to dashboard 2.0, but am still curious to understand what is happening here in order to work around it

Just a quick note to say I'm very focussed on D2.0, 1.0 isn't really my area of expertise, so I'm not in a good position to help here unfortunately.

2 Likes

Of course.
Just wanted you to be aware of this while you develop the template msg listener in dashboard 2.0 :slight_smile:

@dceejay, any clue? I also opened an issue on this in GitHub.
Even if this cannot be fixed at this time, I want to know that I'm not doing something wrong and will implement a workaround.

A couple questions

  1. What version of NR, node.js and dashboard (you can get all this from the startup log)
  2. Are you using Home Assistant or Docker?
  3. You state you are using a ui-template node but your image shows UI Form. Please clarify.
  4. Are you using any ui-control nodes? If so what is it doing?
  5. What browser are you using?

Thanks for reponding.

Right now, I'm using Node JS 21.1.0, Node-red 3.1 & dashboard 3.5, but this happened on previous versions of them too.

Tried it both on Windows & docker

It's just the descriptive name I gave the node. It's ui-template

I don't use ui-control, since it is globally scoped

Typically Edge, but this happens also on Chrome

One thing first, you might want to first try downgrading your node.js to v20 or 18, Odd number node versions are unstable releases

So I've recreated this and can see what you mean but this is over my head, I haven't much experience with $scope.$watch(...)

If anyone else (@hotNipi @Steve-Mcl, @shrickus) wants to look at this, this is a simple flow showing the situation.

[{"id":"1f817680e4e85993","type":"inject","z":"f044b74ff1eb4437","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":230,"y":160,"wires":[["191f465392472653"]]},{"id":"191f465392472653","type":"ui_template","z":"f044b74ff1eb4437","group":"9a0edc351a6b6b2b","name":"","order":1,"width":0,"height":0,"format":"<script type=\"text/javascript\">\nvar $scope = this.scope;\n(function(scope) {\n    $scope.$watch('msg', function(msg)\n    {\n        if (msg)\n            console.log(`Incoming msg: payload=${msg.payload||\"None\"}`);\n            // Process the msg\n    });\n})(scope);\n</script>\n\n<div>\n{{msg.payload}}\n</div>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","className":"","x":440,"y":160,"wires":[[]]},{"id":"533877437c7b77fc","type":"inject","z":"f044b74ff1eb4437","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":240,"y":240,"wires":[["4a200eca06b5435b"]]},{"id":"4a200eca06b5435b","type":"ui_text","z":"f044b74ff1eb4437","group":"e381245bb72a90b1","order":1,"width":0,"height":0,"name":"","label":"text","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":450,"y":240,"wires":[]},{"id":"9a0edc351a6b6b2b","type":"ui_group","name":"page1","tab":"99ad4f0b021bd16f","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"e381245bb72a90b1","type":"ui_group","name":"Group 1","tab":"3ef3649d7051f0f3","order":1,"disp":true,"width":6},{"id":"99ad4f0b021bd16f","type":"ui_tab","name":"With Watch","icon":"dashboard","order":1,"disabled":false,"hidden":false},{"id":"3ef3649d7051f0f3","type":"ui_tab","name":"No Watch","icon":"dashboard","order":3,"disabled":false,"hidden":false}]

Import the flow and then in your browser open the developer's tools/console. Press the inject attached to the ui-template node. Then in the browser window looking at the dashboard select the 'No Watch' and then go back to the 'With Watch' page and you should see the incoming msg: count increasing.

Experiences say that the widget should be created to be as dummy as possible so every payload is treated as true one. Then the server side can be managed to be able to provide valid info for all situation you need an can imagine.

What I think you are seeing is part of the message replay mechanism dashboard has to help re-populate dashboard elements when the user opens a page (so they don't get a blank page). I cannot say I am crazy about it but I understand why it was developed.

I remember having to handle this in the development of the ui-svg node with @BartButenaers (I seem to remember he wrote this up or did a good explanation). I have a distant memory of some crazy workaround, timestamp/cache or something to get past it? (I forget, it was a while ago)

I am not saying it is right or wrong here because the dashboard I am testing this on is running in cloud and is compiled and is v hard to debug unless running un-minified (which I am not).

Hi @Steve-Mcl,
Do you mean this perhaps?
Bart

BTW for client-side message replays, we have used another mechanism in the node-red-contrib-ui-contextmenu node. But that doesn't work when the dashboard framework replays messages from the server-side...

Thanks everyone for the replies. This seems to be indeed an on-load msg replay, even though "Reload last value on refresh" is unchecked.
To work around this bug, I just ignore the replayed messages: upon load the template sends itself a "start" message, and discards any messages received before this "start" message.

<script type="text/javascript">
var $scope = this.scope;
var isReady = false;
var startTime = Date.now();

//Send startup message to self
$scope.send({
    payload: "pageLoad",
    ts: startTime
});

(function(scope) {
    $scope.$watch('msg', function(msg)
    {
        if (msg)
        {
            if (!isReady)
            {
                if (msg.payload === "pageLoad" && msg.ts === startTime)    // this is the "start" msg 
                    isReady = true;
                else
                    console.log(`**Discarding replayed msg: payload=${msg.payload}, ts=${msg.ts}`);
                return;
            }
            // Continue to process messages us usual
            console.log(`Incoming msg: payload=${msg.payload}, ts=${msg.ts}`);
            // process msg
        }
    });
})(scope);
</script>

image

Happy to receive feedback or further suggestions.

1 Like

Yes, that was it. Ta.

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