I am developing a node that lets the user modify an incoming msg.payload via the UI (html table).
By clicking the "save" button - this payload is being sent to the output. This works great so far and the user can see all the changes in the UI.
But here's the problem:
Refreshing the page causes the table to restore the msg.payload that came in before it was modified. How can I update the internals of my node while saving?
BTW: Connecting the (my-node) output to a function node and the function node to the (my-node) input solves the problem - but that's not pretty.
Put a change event handler on each cell of your table, sending the new value every time it is updated. No need to wait and send the whole row of data using a Save button.
The better examples I've seen even use css tricks to provide instant feedback, like setting a background color to green on success, or red on failure.
@shrickus the save button is a 'feature'. The user has to finish before sending the complete data. The whole html/css part already works perfectly, I do show changes with colors and values inside the table data. The table shows changes live and even after clicking send the table contains the modified data (the outgoing payload is fine as well).
But hitting the F5 key will reset the table to the start state, that's the issue here.
But, from an interface perspective refresh not saving changes makes sense. After all, actions might have been taken but the safe button hasn’t been pressed. Thus should from that perspective refresh result in showing the unchanged data from before the editing (aka refresh resulting in undoing the changes), or should refresh result in showing changed data that isn’t saved yet, and if so what makes pressing the save button special?
Is your kind of user intuitively going to expect refresh means changed even without saving, as it is something that differs from most implementations out there in the real world. Like I understand why you want this to happen, but have you thought about wanting it for the right reasons and does it match the logical progression of your interface design.
@afelix ok.. so maybe I wasn't very specific about the actual issue.
Current status:
Changing the data -> not saving the data -> F5 = old values
------- This should be like this and is ok right now
Changing the data -> not saving the data -> leaving the page -> return later = old values
------- This should be like this and is ok right now
Changing the data -> saving the data (new payload is being sent to the output) -> F5 = old values
------- This should not be like this and is the only problem here
@dceejay I have tried this setting already and I wasn't sure if I need to add something else as well.
Changing storeFrontEndInputAsState from false to true, clears the table completely after "saving" and I have to manually inject a new payload to get it back. I am using "<tr ng-repeat="n in msg.payload[0] track by $index" ....".
@hotNipi Well, I think resending the last incoming payload is not bad at all and I respect the underlying logic.
I just thought that there is a possibility to programmatically send a msg to the input inside the myUiNode.js.
Especially because it is quite easy to fix by adding a function node:
May be something like this may work:
Add time (when click was made) to the msg.payload
At the contextmenu ignore incoming messages where time is older than (choose acceptable time)
Morning @hotNipi,
I didn't have time yet to test your proposal, but I'm wondering if we simply could do it like this:
$scope.$watch('msg', function(msg) {
// Ignore undefined messages.
if (!msg) {
return;
}
// Avoid messages being handled twice. Otherwise messages will be replayed after a deploy, causing animations to be started automatically
if (msg.alreadyHandled) {
return;
}
msg.alreadyHandled = true;
...
}
Or do you really think (based on your experience) we need to work with a timestamp? Is this perhaps because we need to allow the same message to be handled multiple times during a (short) time period, e.g. because AngularJs calls the watch multiple times anyway?
Thanks !!
Bart
I think the unwanted replaymessage should be filtered in beforeEmit.
If you change message properties the node-red-dashboard/ ui.js does not know about this change unless you send the message back to the server side. Then you can take advantage of storeFrontEndInputAsState parameter. Haven't tested but just reading the code it should work like this.
from ui.js :
// This is the handler for messages coming back from the UI
var handler = function (msg) {
if (msg.id !== opt.node.id) { return; } // ignore if not us
if (settings.readOnly === true) {
msg.value = currentValues[msg.id];
} // don't accept input if we are in read only mode
else {
var converted = opt.convertBack(msg.value);
if (opt.storeFrontEndInputAsState === true) {
currentValues[msg.id] = converted;
replayMessages[msg.id] = msg;
So I think the unwanted replaymessage should be filtered in beforeEmit <-- wrong. socket emits what is stored. beforeEmit is in use only with new incoming messages.
I did this and it nearly does the trick
$scope.$watch('msg', function (msg) {
if (!msg) {
return;
}
if(msg.handled && msg.handled == true){
console.log('scope already hanlded',msg)
return
}
console.log('scope fresh msg',msg)
var m = { }
m.payload = 'ignore me'
m.topic = msg.topic
m.handled = true
$scope.send(m)
// do your stuff with msg
});
What is wrong about this is that you'll get always that second message back to scope
normal run:
after deploy:
I think proper way is to introduce flag for ui-node's indicating that socket should not send replaymessage.
It is decision for master to even accept the proposal or deny. By looking the code it is a bit more than just "introduce a flag" thing. Needs some deeper analyse and testing but I'm a bit too busy to fit it right away into my near schedule.
EDIT: overall there may be other ways also to get it and we just not smart enough to find it