While working with Bart porting node-red-ui-svg to FlexDash (see also [ANNOUNCE] node-red-contrib-svg-edit: beta 1.0.0 some interesting ideas came up in order to support SVGs in FlexDash. What started as an SVG widget turned into a far more general HTML widget:
- Inject raw HTML into a FlexDash widget and thereby display anything HTML allows
- Send the widget messages to modify what is being displayed using JQuery-like commands
- Since SVG behaves like HTML in this context you can use the HTML node to display and manipulate SVG
Here's what the HTML node+widget supports:
- send it a payload with HTML/SVG and that replaces the content of the widget
- send it messages with JQuery-like commands to edit the HTML/SVG
Demo
Here's a little video showing this in action. What you're seeing is an initial SVG showing a floorplan injected as payload. Then a little flow animates a red and a green circle through the floorplan. The animation is performed by sending JQuery-like commands to the HTML node.
The commands look something like this:
commands: [
{
selector: "#green-dot",
command: "attr",
args: [ "cx", path[ix].x ],
}, {
selector: ".",
command: "attr",
args: ["cy", path[ix].y],
}
]
This is basically the equivalent of
$('#green-dot')
.attr('cx', path[ix].x)
.attr('cy', path[ix].y)
The selector
is a JQuery selector and currently supports tag
, #id
, .class
, [attr=value]
selectors as well as nesting with a space (#red-circle circle
). The .
selector says to reuse the same selector result as for the previous command.
The command is a JQuery function, here the attr()
function to set the 'cx' and 'cy' attributes of the green circle.
The args
are the JQuery arguments, here the attribute name and the value.
The above can be reproduced by installing @tve/node-red-fd-html
and running the flow fetched from https://tve.s3.amazonaws.com/public/fd-svg-demo.json (it's too big to post here due to the SVG).
Implementation
Most likely you imagine that the HTML node works by packaging the incoming messages, sending them to FlexDash, and having FlexDash call JQuery to inject the HTML or perform the commands. That's not at all how it works! It's actually much more sophisticated.
The reason is that the HTML widget follows one of the basic principles of FlexDash which says that all browsers see the same view, even if some arrived late. So what's happening under the hood is that the HTML is built on the server side (i.e. in Node-RED) and is then "reflected" to the browsers. Thus the server always has the current state of the HTML contents and can initialize any browser that connects with the current state. Once initialized, browsers can receive incremental updates.
So the JQuery-like HTML editing messages are processed in Node-RED, not in the browser. Right now the incremental update stuff is not really implemented and I'm shipping the full HTML at every update. Hooking incremental updates into the code is trivial but I'm debating about two different ways to do it and want to experiment more with the two options before deciding. In either case, it's "just a performance optimization" and changes neither the behavior nor the API.
I should add that the node-red-fd-html node is still pretty basic and experimental. The editing UI in the flow editor has a preview of the widget content. It looks something like this:
I put that in on a whim, but I'm not sure how useful it is in the end. I have some ideas, but they may not pan out and I'll remove that again. Suggestions welcome...