Dashboard text input node - avoid global state

Hi folks,

I have used a text input node on my dashboard, where I can enter a message:

image

When I open now an extra dashboard, the same message appears there again automatically.
I can understand that this shared state will be the expected behaviour in most use cases.
However in some cases (e.g. enter a command, send a message, ...) it would be nice if the state wasn't shared. Just like the ui-contextmenu doesn't reply messages on new clients, because we don't want the contextmenu of another user to popup.

Is there any chance this feature will be added, or should I create my own template node with an input element inside?

Thanks !!!
Bart

Anybody an idea about what I'm doing wrong, or how I can solve this?

Do you mean you open a new browser window to the same dashboard tab?

Hey Paul,

Yes when I have some user input in the dashboard on the first browser window, it is nicely synced with the dashboard in the other browser window:

But I just want to enter some text (e.g. a message, command, ...) on the first window and when I click a button then I want to do something on the server with that text. So I don't want to have my text appear on other browser windows...

I'm now wondering if this is because the dashboard is not multi-user? Perhaps this is not possible, since every widget has state on the server, that is shared by all clients. But with the contextmenu node we have worked around that, because when you open a contextmenu in browser 1 you don't want it to appear also on browser 2. Therefore I thought something like that was also possible with the text-input node somehow...

Perhaps I need to use a template button with an "input" element, but not sure how to set the correct style...

I'd be willing to bet it is. If you put a slider connected to a gauge you will see the same effece. Move the slider in one browser window and the slider and gauge will move in the other window.

(Hmmm, I wonder if this could be used in a magic trick?)

I tried to workaround it, by creating a Dashboard Template node with a custom "input" element.

When I inspect an original Dashboard input node, then it looks like this:

image

So I have copied this part (label and input elements) to my Template node:

<label ng-bind-html="me.item.getLabel()" class="ng-binding" for="input_1">Notification body</label>
<input ng-model="me.item.value" ng-model-options="{'timezone':'UTC'}" ng-change="me.valueChanged(300)" ng-trim="false" aria-label="Notification body" type="text" style="z-index: 1;" ng-style="(me.item.mode==='color') &amp;&amp; {'padding-left':'25%'}" step="any" class="ng-valid md-input ng-touched ng-not-empty ng-dirty ng-valid-parse" id="input_1" aria-invalid="false">

I had expected to have the same look and feel as the original Input node, but it looks completely different:

image

I "assume" it has somehow to do with the difference in the md-card:

I assume I need to do some changes to the dashboard style (which @hotnipi had shared in the past on this forum), because I don't see any possibilities in the Template node config screen. But not sure how to do that ....

Can anybody guide this CSS noob in the right direction?

If you add a configuration option in https://github.com/node-red/node-red-dashboard/blob/master/nodes/ui_text_input.js#L17 that sets
storeFrontEndInputAsState: false
then you should get what you want.

I think the chances of getting such a PR through should be high, not?

not the md-card is relevant, but md-input-container, somthing like

<md-input-container class="flex">
    <input type="text">
</md-input-container>

should get you in the right direction

Hey @cinhcet,
I totally wasn't aware that this was possible. Thanks a lot for the tip!!!!!!!!
Will try that this evening...

I had considered that also, because I manipulate the storeFrontEndInputAsState for some of my UI nodes (e.g. ContextMenu node). Indeed sharing state doesn't make sense for those nodes. But I assume for the Input-node this flag value might depend on the use case. So you should be able to set it true or false (and to not break existing flows which use shared state), which means an extra checkbox on the config screen I suppose...

So - in the wider context - which other widgets could this sensibly apply to ? Thinking about it I can't think of any - as to my mind all others also double up as display type indicators and so you would always want them to represent the true backend state, whereas for text input I can see this limited use case for "input only" type usage - where leaving the old value around isn't much use. thoughts please.

I have added nr-dashboard-textinput to the style of your input container, to get a white text:

<md-input-container class="flex nr-dashboard-textinput">
    <label for="notification_title">Notification Title</label>
    <input id="notification_title" aria-label="Notification title" type="text">
</md-input-container>

Don't know if that is the best way to do that...
But as soon as I enter text, the label color becomes ugly blue for some reason, while it should be green (like in the standard Input node):

template_style

For a dashboard Input-node, I see this in my developer tools:

image

Do you have any idea how I can apply that theme color to my template?

Thanks Dave for at least taking this into consideration!
I absolutely agree that this only applies to input type usage. But I need to think about this, before asking you to implement something that perhaps nobody can/will use...
Because I'm afraid that I'm a bit cheating at the moment. I need to build a demo for a UI node, and lots of users might use that demo (simultaneously). So in fact I'm building a multi-user demo on a non-multi-user dashboard. So not the best option, but there is no other way for me to do it (since my UI node only runs in this dashboard)... :roll_eyes:
For now I will try to workaround it using the proposal from @cinhcet. Once my demo is up and running, I will discuss it here to see if we can find a standard solution...

this has to go in a parent element (look at the css code of the ui-input)
Quick hack:

<md-card class="nr-dashboard-textinput">
    <md-input-container class="md-block has-label" style="padding:0">
        <label>Notification Title</label>
        <input type="text">
    </md-input-container>
</md-card>

Not completely equivalent, but close enough, I would say?

1 Like

Yes I had looked at the ui-input. That is where I got the idea to add nr-dashboard-textinput.
But I had no idea at all that an md-card nested inside another md-card would work:

image

And it seems indeed to be working fine, for those who ever need it:

template_style2

Just marvellous. Thank you very much !!!!

1 Like

There were some small differences between the Template node and the Input node.

image

So after a heavy battle with the "Style" tabsheet in my Chrome developer tools, I updated the Template node code to this:

<md-card class="nr-dashboard-textinput" style="margin-top:8px; margin-bottom:0px; margin-left:6px; margin-right:6px">
    <md-input-container class="md-block has-label" style="padding:0px; margin:0px">
        <label style="padding:0px">Notification Title</label>
        <input type="text">
    </md-input-container>
</md-card>

Now it looks quite the same as the Input-nodes:

image

This is a great example.
However, how are you then grabbing the entered text? I haven't been able to work that out as I do not want to use a button but rather the same that NR does either by a tab key or enter key being pressed and do not know Angular well enough.

If anybody ever needs it, I had another Template node to display a button. In that Template node, I send the values of the Text Input fields to the server as a message (as soon as the button is clicked):

<md-button ng-click="onClick()">Send text to Node-RED</md-button>

<script>
(function(scope) {
    scope.onClick= function() {
        // Here I read the values from the my custom text input field (which needs to have id="my_unique_id")
        var myTextInputElement = document.getElementById('my_unique_id').value || "My default text";
        
        // Send a message on the output of the Button Template node, containing the value of the text input field
        scope.send({
            my_text: myTextInputElement
        });
    }
})(scope);
</script>

Remark: since this code snippet tries to find a text input element on your dashboard with id "my_unique_id", don't forget to apply that id to your text input element:

<md-card class="nr-dashboard-textinput" style="margin-top:8px; margin-bottom:0px; margin-left:6px; margin-right:6px">
    <md-input-container class="md-block has-label" style="padding:0px; margin:0px">
        <label style="padding:0px">Notification Body</label>
        <input type="text" id="my_unique_id">
    </md-input-container>
</md-card>

In case of multiple text input elements, you need to give each element its own unique id!

Based on the above onClick function, I had thought your question could be solved by changing my Text Input Template node code like this:

<md-card class="nr-dashboard-textinput" style="margin-top:8px; margin-bottom:0px; margin-left:6px; margin-right:6px">
    <md-input-container class="md-block has-label" style="padding:0px; margin:0px">
        <label style="padding:0px">Notification Body</label>
        <input type="text" id="notification_body" onkeydown="handleOnkeydown(this)">
    </md-input-container>
</md-card>

<script>
(function(scope) {
    scope.handleOnkeydown= function(inputElement) {
        // Send the text value to the server, when the Enter key is being pressed
        if(event.key === 'Enter') { 
            send({value: inputElement.value}); 
        }
    }
})(scope);
</script>

But it gives me the following error:

image

Does anybody know how to solve this?

I copied pasted that code snippet too quickly last evening. I have now edited my code snippet above and added some explanation about "my_unique_id". Should work now.

Unfortunately I don't know what I have done wrong in that example. Hopefully somebody else can help us with this ...

@BartButenaers I have spent some hours on trying to identify this and so far without any success. Will keep at it.

Had hoped that somebody with AngularJs knowledge had joined the party...
Anyway did some further experiments this evening, and this works for me:

<md-card class="nr-dashboard-textinput" style="margin-top:8px; margin-bottom:0px; margin-left:6px; margin-right:6px">
    <md-input-container class="md-block has-label" style="padding:0px; margin:0px">
        <label style="padding:0px">Notification Body</label>
        <input type="text" ng-keydown="handleOnkeydown($event)">
    </md-input-container>
</md-card>

<script>
(function(scope) {
    scope.handleOnkeydown= function(event) {
        // Send the text value to the server, when the Enter key is being pressed
        if(event.key === 'Enter') {
            var inputElement = event.target || event.srcElement;
            scope.send({payload: inputElement.value}); 
        }
    }
})(scope);
</script>

Please let me know if it does the job in your use case!

[EDIT] Note that you can also use if(event.keyCode === 13) instead of if(event.key === 'Enter')

@BartButenaers - I was getting closer after much Googling but I was still missing a final piece.
I will check this out further this afternoon but it appears to be just fine. From what I was reading keyCode is deprecated so event.key is the way to go.

This was the piece I was missing. I couldn't find any doc.
var inputElement = event.target || event.srcElement;
scope.send({payload: inputElement.value});

Thanks for the solution. Much appreciated.

1 Like