Skip to content

Installable Widget Node

Hiroyasu Nishiyama edited this page Mar 28, 2018 · 3 revisions

Make Node-RED Dashboard widgets installable

Currently, Node-RED Dashboard do not allow installing user created widget nodes. Instead, it supports TEMPLATE node which allows embedding HTML code in its setting panel. However, making new widget node installable is desirable because it enriches Node-RED Dashboard ecosystem as of current Node-RED nodes.

How to make Node-RED Dashboard widges installable

  • Basic node code (HTML and JavaScript) structure follows those of template node (but can be arbitrary),
  • Support code generation from existing TEMPLATE node using nodegen tool (optional).

Problems & Proposal

  1. Widget node is basically same as ordinary Node-RED node. Different point is that it requires accessing ui.js module of Node-RED Dashboard for accessing UI interfaces (e.g. ui.add). Because us.js is stored under node-red-dashboard module, we need some means for accessing it.

    Proposal:

    1. Add interface (e.g. getDashboardPath) for getting module install path of node-red-dashboard.

      • Can access other data stored under node-red-dashboard such as icons.
      • As pointed on the last teleconference, this interface is too specialized for Node-RED Dashboard.
    2. Add interface (e.g. getModulePath) for getting module installation path (or other information if there are good usage) of any module.

      • This proposal is generarization of #1.
    3. Add interface for accessing ui.js interface from node API (i.e. RED variable) passed on node initialization.

      • Similar to #1, this proposal is too specialized for Node-RED Dashboard.
    4. other better way for accessing ui.js?

    Currently, we prefer #2 because this one is more general compared to other proposal.

  2. Accessing interfaces of us.js must be delayed after node-red-dashboard is loaded.

    Proposal:

    • Delay require of ui.js until widget node deployment.

Node Code Structure

Excerpt from Generated Code by Prototype Implementation

  • HTML code

    <script type="text/x-red" data-template-name="ui_hello">
       ... help text ...
    </script>
    
    <script type="text/javascript">
        function mk_conf(NAME) {
           ... create node definition ...
        };
    
        RED.nodes.registerType('ui_hello', mk_conf('hello'));
    </script>

  • JavaScript code

    module.exports = function(RED) {
    
        var TEMPLATE_SCOPE = "local";
        // HTML code for widget
        var TEMPLATE = '// name: hello\n<div style=\"background-color:#84180F; color:#FFFFFF; border-radius:15px; font-size:20px;\" align=\"center\">\n    Hello Node-RED\n</div>\n';
        var STORE_OUT_MSGS = true;
        var FWD_IN_MSGS = true;
    
        var ui = undefined;
        
        function TemplateNode(config) {
            if(ui === undefined) {
                // load ui.js module
                var base = RED.nodes.getModulePath("node-red-dashboard");
                ui = require(base+'/ui')(RED);
            }
            RED.nodes.createNode(this, config);
            var node = this;
            var group = RED.nodes.getNode(config.group);
            if (!group && TEMPLATE_SCOPE !== 'global') { return; }
            var tab = null;
            if (TEMPLATE_SCOPE !== 'global') {
                tab = RED.nodes.getNode(group.config.tab);
                if (!tab) { return; }
                if (!config.width) {
                    config.width = group.config.width;
                }
            }
            var hei = Number(config.height || 0);
            // initialize UI widget
            var done = ui.add({
                forwardInputMessages: FWD_IN_MSGS,
                storeFrontEndInputAsState: STORE_OUT_MSGS,
                emitOnlyNewValues: false,
                node: node,
                tab: tab,
                group: group,
                control: {
                    type: 'template',
                    order: config.order,
                    width: config.width || 6,
                    height: hei,
                    format: TEMPLATE,
                    templateScope: TEMPLATE_SCOPE
                },
                ...
            });
            node.on("close", done);
        }
        RED.nodes.registerType('ui_hello', TemplateNode);
    };

Comment from DCJ:

The problem that I haven't worked out is that it isn't just about ui.add.

Quite a few widgets have extra code in src/components/ui-component/templates/ui-component-ctrl.js

Could this also be abstracted back up to ui.add in some way ? and thus allow nodes to add their own functions there instead ? (I've not scrutinised it enough to know yet). As many of these are to do with initialising the widgets internal functions would this conflict with proposal 2) re the timing of loading and create a sequencing issue ? (or would it help tidy it up ?)

In some ways option ii) is too broad - it could allow any extra node to try to build on top of any other node - I think while we possibly do want to do this - I think we want to restrict which nodes extend what, or have some way to stop nodes being extended unless they explicitly declare they can be.

Finally I go back to the overall "bigger picture" thought... Is dashboard the right thing to extend ? - seeing as it is based on Angular 1 - and we don't want to move to typescript / Angular4,5,etc