Can we use Mustache with the UI builder

Hi there,

A bit new too Node-Red, I'm trying to access the msg.payload in the index.html inside the UI-builder node. There's a dropdown menu at the bottom right of the UI-builder's interface.

So ie:
UI-builder doesn't work with {{}} to inject dynamic data but we can setup a CSS html and js file in it. There is a very long a complicated way to access the msg.payload via the index.js file as well as changing content in the index.html file via "document.getElementById('msg')"
BUT surely if mustache works on the other too templet nodes then it should work on the UI-builder node(which would simplify things)???

The template node under functions does work with {{}} but won't do custom CSS HTML and JS

And the template node under dashboard will do {{}} but not CSS JavaScript and HTML

The form node under dashboard is simple and quick but limited and no custom CSS JavaScript or HTML

:man_facepalming: it's like I'm chasing my tail
I've google but I'm not getting any where, could any one please guild me in the right direction?
Thanks.

Hi Armant, welcome to the forum.

I'm afraid that you've slightly misinterpreted how things are working with uibuilder. The picture you posted is of the file editor which is a convenience feature of uibuilder. The files being edited are just that, files, those files will be served by the uibuilder web server.

So that means that those files can't use Mustache in the same way that the template node does for example. The template node is NOT a file editor. The code is server code and so the Mustache variables are replaced on the server before being presented to the client front-end.

However, all is not lost by any means. :slight_smile: You can certainly use Mustache if you wish. The difference being that you will need to load the appropriate mustache client library in your index.html file. Then in your index.js file, add the code to do the mustache replacements inside the onChange function for when a msg arrives.

As of uibuilder v5, there is another possibility. While not Mustache, uibuilder v5 does allow you to turn on server-side processing using EJS. Details of how to do that in the tech docs.

However, generally, this isn't really needed since dynamic updates in the browser client is so simple with uibuilder. In fact, it isn't hard to use uibuilder with just plain DOM without needing any other libraries.

In index.html

<div>
   <p id="elementid">This is static text</p>
</div>

In index.js

// Pseudo jQuery-like selector
if (!window.$) {
    window.$ = document.querySelector.bind(document)
}

$('#elementid').innerText = 'Oooh, dynamically set'

or, more completely

// Pseudo jQuery-like selector
if (!window.$) {
    window.$ = document.querySelector.bind(document)
}

uibuilder.start()

uibuilder.onChange('msg', (msg) => {
    $('#elementid').innerText = msg.payload
})

UPDATE 18:10 - a quick and dirty example of using mustache with uibuilder in the front-end:

flow

[
    {
        "id": "dafceb486bae652c",
        "type": "inject",
        "z": "c90d1cebe99c8390",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "Msg from Node-RED",
        "payload": "{\"name\":\"Fred\"}",
        "payloadType": "json",
        "x": 330,
        "y": 140,
        "wires": [
            [
                "9416d117d9b657d2"
            ]
        ]
    },
    {
        "id": "0779feee345e245a",
        "type": "debug",
        "z": "c90d1cebe99c8390",
        "name": "debug 1",
        "active": true,
        "tosidebar": false,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "counter",
        "x": 715,
        "y": 120,
        "wires": [],
        "l": false
    },
    {
        "id": "1e42f5c66891576e",
        "type": "debug",
        "z": "c90d1cebe99c8390",
        "name": "debug 2",
        "active": true,
        "tosidebar": false,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "counter",
        "x": 715,
        "y": 180,
        "wires": [],
        "l": false
    },
    {
        "id": "9416d117d9b657d2",
        "type": "uibuilder",
        "z": "c90d1cebe99c8390",
        "name": "",
        "topic": "",
        "url": "mustache",
        "fwdInMessages": false,
        "allowScripts": false,
        "allowStyles": false,
        "copyIndex": true,
        "templateFolder": "blank",
        "extTemplate": "",
        "showfolder": false,
        "reload": false,
        "sourceFolder": "src",
        "deployedVersion": "5.0.2",
        "x": 590,
        "y": 140,
        "wires": [
            [
                "0779feee345e245a"
            ],
            [
                "1e42f5c66891576e"
            ]
        ]
    }
]

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Blank template - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - Blank template">
    <link rel="icon" href="./images/node-blue.ico">

    <!-- Your own CSS -->
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

</head><body class="uib">
    <script id="mp_template" type="text/template">
        <p>Hello {{ name }}</p>
        <p>This was from a script template</p>
    </script>
    
    <h1>uibuilder Mustache</h1>

    <div id="mypanel">This will be updated from a script templatewhen a msg is received.</div>
    
    <div id="mypanel2">This will be updated from a JS template when a msg is received.</div>

    <button onclick="fnSendToNR('A message from the sharp end!')">Send a msg back to Node-RED</button>

    <pre id="msg" class="syntax-highlight">Waiting for a message from Node-RED</pre>

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <script src="../uibuilder/vendor/socket.io/socket.io.js">/* REQUIRED: Socket.IO is loaded only once for all instances. Without this, you don't get a websocket connection */</script>
    <script src="./uibuilderfe.min.js">/* REQUIRED: remove 'min.' to use dev version */</script>
    <script src="https://cdn.jsdelivr.net/npm/mustache/mustache.min.js"></script>
    <script src="./index.js">/* OPTIONAL: Put your custom code here */</script>
    <!-- #endregion -->

</body></html>

index.js

/* eslint-disable strict */
/* jshint browser: true, esversion: 6, asi: true */
/* globals uibuilder */
// @ts-nocheck

/** Minimalist code for uibuilder and Node-RED */
'use strict'

// return formatted HTML version of JSON object
window.syntaxHighlight = function (json) {
    json = JSON.stringify(json, undefined, 4)
    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
    json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
        var cls = 'number'
        if ((/^"/).test(match)) {
            if ((/:$/).test(match)) {
                cls = 'key'
            } else {
                cls = 'string'
            }
        } else if ((/true|false/).test(match)) {
            cls = 'boolean'
        } else if ((/null/).test(match)) {
            cls = 'null'
        }
        return '<span class="' + cls + '">' + match + '</span>'
    })
    return json
} // --- End of syntaxHighlight --- //

// Send a message back to Node-RED
window.fnSendToNR = function fnSendToNR(payload) {
    uibuilder.send({
        'topic': 'msg-from-uibuilder-front-end',
        'payload': payload,
    })
}

// Set up a Mustache template in JavaScript
var template = '<p>This is from a template defined in JavaScript</p><p>Hello {{ name }}</p>'

// Start up uibuilder - see the docs for the optional parameters
uibuilder.start()

// Listen for incoming messages from Node-RED
uibuilder.onChange('msg', function(msg){
    console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)

    // dump the msg as text to the "msg" html element
    const eMsg = document.getElementById('msg')
    eMsg.innerHTML = window.syntaxHighlight(msg)

    // Update mypanel with templated output
    var template = document.getElementById('mp_template').innerHTML
    var text = Mustache.render(template, msg.payload)
    console.log('Script Template text: ', text)
    document.getElementById('mypanel').innerHTML = text

    // Update mypanel2 with templated output
    document.getElementById('mypanel2').innerHTML = Mustache.render(template, msg.payload)
})