I recently needed to indtroduce a dialog box to one of my dashboards, and tried to use the "Notification" node for this, but it didn't seem to be a good fit. What was missing to me was 1) a way to interrupt the flow of a message to ask for confirmation 2) a second output from which the original message would emit if (and only if) the dialog response was "Ok" 3) a way to enter and compare a password with a stored hash 4) the ability to substitute a custom template for the dialog. I thought through my requirements, and explored the "Notification" node's source code, and came to the conclusion that a separate node which only deals with md-dialog
might be preferable to making changes to the existing node (I get the impression this node was first only intended to deal with md-toast
messages, and that the md-dialog
functionality was added as an afterthought).
At first I thought I'd build this as a "contrib" node, but it seems the required classes are not exported; particularly the ui.add
method is missing from the scope, which is needed to create these dialogs. Yes, I've seen other solutions, with a hidden node in a dashboard group, but this doesn't seem right to me. So I gritted my teeth and jumped in by cloning node-red-dashboard and making my additions there. This is still very much work in progress, but I've come to a point where I can share a working draft of my ideas and get some feedback.
The "Dialog" node (copied from the help file):
Inputs
payload
string
The msg.payload
is not used by the dialog node, but will be passed through to the second output under certain conditions (see Output #2 below).
title
string
Title of the dialog.
text
string
Text message to display in the dialog.
html
string
HTML formatted message to display in the dialog. (not yet implemented!)
values
varies
The initial value in a "prompt" dialog,or an object with name:value pairs if the dialog has multiple fields (see "Custom fields" below).
fields
object
An object describing the fields to show in the dialog (see "Custom fields" below).
Output #1
payload
varies
The msg.payload
on output #1 can be different types:
-
true
, if the dialog is an "alert" dialog. -
true
, if the dialog is a "confirm" dialog, and primary action is taken (e.g. "Ok"). - the entered value, if the dialog is a "prompt" and primary action is taken (e.g. "Ok").
- an object containing the entered values, if the dialog is a "prompt" containing multiple fields, and primary action is taken (e.g. "Ok").
-
true
, if this is a "passcode" dialog, the correct passcode was entered, and primary action is taken (e.g. "Ok") -
false
, in all other cases.
Output #2
The second output will output the incoming message, but only if the primary action (e.g. "Ok") was chosen. Furthermore, the incoming message will be dropped if the dialog is a prompt dialog and no value(s) have been entered, or if it is a passcode dialog and an incorrect passcode has been entered.
Details
Shows msg.text
as a popup dialog
Sending a blank payload will remove any active dialog without sending any data.
Basic usage
Four different dialog types are available:
Alert
A basic alert box which displays a message and can be dismissed with a button.
Confirm
Displays a message and asks the user to confirm (primary action) or abort (secondary action).
Prompt
Asks for user text input.
Passcode
Asks for a passcode. A master passcode can be supplied either as a local plain text cred
, or as an SHA-256 hashed local, flow or global string
.
Custom fields
Custom fields can be provided for a "prompt" dialog by supplying a msg.fields
parameter describing the fields. The initial value of a field can be set with msg.values
. The fields supported by the default template include:
Text input
msg.fields = [{
"type": "text",
"name": "myText",
"label": "My text input"
}]
msg.values = {
"myText": "My initial value"
}
Password input
msg.fields = [{
"type": "password",
"name": "myPass",
"label": "My password input"
}]
msg.values = {
"myPass": "My initial password"
}
Select box
msg.fields = [{
"type": "select",
"name": "mySelect",
"label": "A select box",
"options": [{
"name": "Option One",
"value": 111
},{
"name": "Option Two",
"value": 222
}]
}]
msg.values = {
"mySelect": 222
}
Radio buttons
msg.fields = [{
"type": "radio",
"name": "myRadio",
"label": "A radio button group",
"options": [{
"label": "Radio One",
"value": "R1"
},{
"label": "Radio Two",
"value": "R2"
}]
}]
msg.values = {
"myRadio": "R2"
}
Checkboxes
T.b.d.
Date input
msg.fields = [{
"type": "date",
"name": "myDate",
"label": "A date input"
}]
msg.values = {
"myDate": new Date().toLocaleDateString()
}
Note that it is not currently possible to specify which fields are "required", or change the action buttons, without supplying a custom template (see below).
Custom templates
A custom dialog template can be substituted by specifying a "File" to use, or by entering a "Custom" template directly in the configuration window. Certain Angular elements are required to make a fully functioning template:
<md-dialog>
Container element for the entire dialog.
<md-dialog-content>
Container element for the dialog title, text and field(s).
{{ dialog.title }}
Contents of the msg.title parameter
{{::dialog.textContent}}
Contents of the msg.text parameter
ng-bind-html="::dialog.mdHtmlContent | trusted"
Binds the contents of the msg.html parameter to an Angular container (e.g. a <div>)
<md-dialog-actions>
Container element for the action buttons
<md-button ng-click="dialog.abort()">{{ dialog.cancel }}</md-button>
Action button to dismiss the dialog (e.g. "Cancel").
<md-button ng-click="dialog.hide()">{{ dialog.ok }}</md-button>
Action button to confirm the dialog (e.g. "Ok")
Some screenshots:
Unfinished bits:
- The default template renders a bit weirdly with certain fields
- The default template logic needs improving, it's not very elegant
- Add support for htmlContent (in addition to textContent)
- Make title, textContent and htmlContent settable in the config
- Give fields a "required" flag and enforce it
- Add support for field validator functions
- Bugs! There are some - and maybe you can help me find more!
- Tests!?
Look forward to hearing what people - and dashboard core team in particular - think about this new node. Just please be gentle; I've spent a fair amount of time on this.
Edit: The relevant files are
src/main.js the mdDialog magic happens here
nodes/ui_dialog.html the node config window
nodes/ui_dialog.js the node logic
nodes/ui_dialog.md my messy dev notes (will be removed)
src/partials/dialog.html the default template
nodes/locales/en-US/ui_dialog.json i18n strings
nodes/locales/en-US/ui_dialog.html help text