Mustache table coding question

I have spent multiple hours today working on this. :frowning:
I'm using a mustache template node to render a database query results page as HTML.
I think what I want is simple ... if the string in the return value contains an 'h' then change the background color of that cell to light red/pink.

I have tried all sorts of things and can't get anything to work. I even asked, or tried to, Chat GPT about it and got nothing that would even come close to running. GPT was trying to get me to create script functions and call them from mustache in a way that was very confusing to me. It looked like it was trying to building the page "in memory" then when it was all done it would "render" it as HTML. I could not understand what it was doing and the "explanation" was even more confusing.

Here is a simple sample of what I'm trying to do. Very simple data set and page.

[
    {
        "id": "d115287648de5277",
        "type": "inject",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[{\"w1\":\"This\",\"w2\":\"is\",\"w3\":\"Big!\"},{\"w1\":\"Big\",\"w2\":\"as\",\"w3\":\"this?\"}]",
        "payloadType": "json",
        "x": 110,
        "y": 80,
        "wires": [
            [
                "02a317e4972a9954"
            ]
        ]
    },
    {
        "id": "8c71a0febf7bcd4d",
        "type": "debug",
        "z": "923dd5a9f9e0a06a",
        "name": "debug 8",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 660,
        "y": 60,
        "wires": []
    },
    {
        "id": "3cef1bf1ead9baa3",
        "type": "http in",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "url": "/ws2/test",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 130,
        "y": 120,
        "wires": [
            [
                "6d20ca2e5c43ccaf"
            ]
        ]
    },
    {
        "id": "6c988531eedbd0c3",
        "type": "http response",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 650,
        "y": 100,
        "wires": []
    },
    {
        "id": "6d20ca2e5c43ccaf",
        "type": "function",
        "z": "923dd5a9f9e0a06a",
        "name": "function 2",
        "func": "var data = [\n    { \"w1\": \"This\", \"w2\": \"is\", \"w3\": \"Big!\" },\n    { \"w1\": \"Big\", \"w2\": \"as\", \"w3\": \"this?\" }\n];\n\nmsg.payload = data;\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 300,
        "y": 120,
        "wires": [
            [
                "02a317e4972a9954"
            ]
        ]
    },
    {
        "id": "02a317e4972a9954",
        "type": "template",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <title>\n            Moustache Test\n        </title>\n        <style>\n            table, td, th {\n                text-align:center;\n                font-family:monospace;\n                font-size: 16px;\n            }\n            td { width: 70px; }\n        </style>\n    </head>\n    <body>\n        <h3>\n            <center>\n                Moustache Test Page\n            </center>\n        </h3>\n        <table  align=\"center\">\n            <tr>\n                <td>Word 1</td><td>Word 2</td><td>Word 3</td>\n            </tr>\n            {{#payload}}\n            <tr>\n                <td bgcolor=\"{{ '{{w1}}'.includes('h') ? '#FFC0CB' : '' }}\">{{w1}}</td>\n                <td bgcolor='#FFC0CB'>{{w2}}</td>\n                <td bgcolor=\"{{w3}}.includes('h') ? '#FFC0CB' : ''\">{{w3}}</td>\n            </tr>\n            {{/payload}}\n        </table>\n    </body>\n</html>\n",
        "output": "str",
        "x": 480,
        "y": 80,
        "wires": [
            [
                "8c71a0febf7bcd4d",
                "6c988531eedbd0c3"
            ]
        ]
    },
    {
        "id": "58e1af2d0f632963",
        "type": "comment",
        "z": "923dd5a9f9e0a06a",
        "name": "Description",
        "info": "What I want to do sounds simple.\nIf the string data has a 'h' in it then I want the back ground of the cell\nto be a light red / pink, otherwise white as the default.\n\nI have tried all sort of ways and even asked GPT (or tried to, I never got\nthe question close enough to give me workable code.)\n",
        "x": 300,
        "y": 60,
        "wires": []
    }
]

might be the same issue i had a while back.

I've found you cant do logic in mustache templates. Only static stuff works.

Thanks, Meeki
My search didn't your thread when I tried. (in hindsight I think I typo'ed "mustache" :slight_smile: )

Now I'm thinking that I should re-swizzle the data in the function node to have each work be an object with two attributes, the data and a flag base on the 'h' search. Then in mustache each cell would look like:

<td bgcolor="{{w1.flag}} ? '#FFC0CB' : '' ">{{w1.data}}</td>

Where flag would be true/false ... thanks for jogging that memory!!

1 Like

Too many times I've been burned by the mustache
Also I now have 2 nodes instead of just one function node.

I've just given up on mustache and all my stuff is done in function.
example:

msg.payload = '<!DOCTYPE html>' +
    '<html>' +
    '<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"></script>' +
    '<body>' +
    '<canvas id="myChart" style="max-width: 96%;"></canvas>' +
    '<script>' +
        'new Chart("myChart", {' +
            'type: "line",' +
            'data: {' +
            'labels: [' + chartjs_labels + '],' +
                'datasets: [{' +
                'label: "°C",' +
                    'borderColor: "olivedrab",' +
                    'borderWidth: 1,' +
                    'fill: false,' +
        'data: [' + chartjs_data_temperature + ']' +
                '}]' +
            '},' +
            'options: {' +
                'legend: {' +
                    'display: false' +
                '},' +
                'scales: {' +
                    'xAxes: [{' +
                        'type: "time",' +
                        'distribution: "linear",' +
                        'display: false' +
                    '}]' +
                '}' +
            '}' +
        '});' +
    '</script>' +
    '</body>' +
    '</html>';

Oh its a total pain sometimes when you have a typo and it is not highlighted nice by the editor ..... gerrrrrrr

Hope you will forgive the intrusion. Just to say that you can use UIBUILDER to create an HTML table assuming your SQL output results in either an array of objects or an object of objects. An array of objects is the normal standard for tabular data in JavaScript so I'd expect the SQL nodes to output it.

You can use the uib-element node set to produce a table output and feed that to the uib-html node to turn it into HTML, all in Node-RED. You can also use an intermediary template node (the core Node-RED one) to supply a wrapping template in which the table will be embedded.

Here you see it in use without the wrapping template because I'm only creating a routing file for uibuilder's front-end router so the index page provides the wrapper:

It can even be used to create output to feed into a Dashboard ui_template node:

1 Like

Problem resolved using code and template. This may be a kludge but it works for me, easy to understand. If you are interested in my solution ...

[
    {
        "id": "d115287648de5277",
        "type": "inject",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[{\"w1\":\"This\",\"w2\":\"is\",\"w3\":\"Big!\"},{\"w1\":\"Big\",\"w2\":\"as\",\"w3\":\"this?\"}]",
        "payloadType": "json",
        "x": 630,
        "y": 820,
        "wires": [
            [
                "02a317e4972a9954"
            ]
        ]
    },
    {
        "id": "3cef1bf1ead9baa3",
        "type": "http in",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "url": "/ws2/test",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 630,
        "y": 860,
        "wires": [
            [
                "0f865cc80c7dc24a"
            ]
        ]
    },
    {
        "id": "6c988531eedbd0c3",
        "type": "http response",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 1110,
        "y": 820,
        "wires": []
    },
    {
        "id": "02a317e4972a9954",
        "type": "template",
        "z": "923dd5a9f9e0a06a",
        "name": "",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <title>\n            Mustache Test\n        </title>\n        <style>\n            table, td, th {\n                text-align:center;\n                font-family:monospace;\n                font-size: 16px;\n            }\n            td {\n                width: 70px;\n            }\n        </style>\n    </head>\n    <body>\n        <h3>\n            <center>\n                Mustache Test Page\n            </center>\n        </h3>\n        <table  align=\"center\">\n            <tr>\n                <td>Word 1</td><td>Word 2</td><td>Word 3</td>\n            </tr>\n            {{#payload}}\n            <tr>\n                {{#w1.hit}}<td style=\"background-color : #FF6633\">{{w1.data}}</td>{{/w1.hit}}{{^w1.hit}}<td>{{w1.data}}</td>{{/w1.hit}}\n                {{#w2.hit}}<td style=\"background-color : #FF6633\">{{w2.data}}</td>{{/w2.hit}}{{^w2.hit}}<td>{{w2.data}}</td>{{/w2.hit}}\n                {{#w3.hit}}<td style=\"background-color : #FF6633\">{{w3.data}}</td>{{/w3.hit}}{{^w3.hit}}<td>{{w3.data}}</td>{{/w3.hit}}\n            </tr>\n            {{/payload}}\n        </table>\n    </body>\n</html>\n",
        "output": "str",
        "x": 960,
        "y": 820,
        "wires": [
            [
                "6c988531eedbd0c3"
            ]
        ]
    },
    {
        "id": "58e1af2d0f632963",
        "type": "comment",
        "z": "923dd5a9f9e0a06a",
        "name": "Description",
        "info": "What I want to do sounds simple.\nIf the string data has a 'h' in it then I want the back ground of the cell\nto be a light red / pink, otherwise white as the default.\n\nI have tried all sort of ways and even asked GPT (or tried to, I never got\nthe question close enough to give me workable code.)\n",
        "x": 780,
        "y": 800,
        "wires": []
    },
    {
        "id": "0f865cc80c7dc24a",
        "type": "function",
        "z": "923dd5a9f9e0a06a",
        "name": "function 3",
        "func": "var results = [\n    { \"w1\": \"This\", \"w2\": \"is\", \"w3\": \"Big!\" },\n    { \"w1\": \"Big\", \"w2\": \"as\", \"w3\": \"this?\" }\n];\n\nmsg.payload = results;\n\nvar theNewData = [];\n\nfor (var row = 0; row < msg.payload.length; row++) {\n    var newRow = {};\n    var tw1 = {};\n    tw1.data = msg.payload[row].w1;\n    tw1.hit = tw1.data.includes('h') ? true : false;\n    newRow.w1 = tw1;\n\n    var tw2 = {};\n    tw2.data = msg.payload[row].w2;\n    tw2.hit = tw2.data.includes('h') ? true : false;\n    newRow.w2 = tw2;\n\n    var tw3 = {};\n    tw3.data = msg.payload[row].w3;\n    tw3.hit = tw3.data.includes('h') ? true : false;\n    newRow.w3 = tw3;\n\n    theNewData.push(newRow);\n}\n\nmsg.theNewData = theNewData;\n\nmsg.payload = theNewData;\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 800,
        "y": 860,
        "wires": [
            [
                "02a317e4972a9954"
            ]
        ]
    }
]

Thanks for the help guys! Sometimes it just takes someone saying the right words to get to the aHA! :slight_smile:

You could probably shorten your mustache to

{{#payload}}
    <tr>
        <td{{#w1.hit}} style="background-color : #FF6633"{{/w1.hit}}>{{w1.data}}</td>
        <td{{#w2.hit}} style="background-color : #FF6633"{{/w2.hit}}>{{w2.data}}</td>
        <td{{#w3.hit}} style="background-color : #FF6633"{{/w3.hit}}>{{w3.data}}</td>
    </tr>
{{/payload}}

You could also make the function more dynamic, so you can feed in array of objects with as many properties as you like, without having to edit the function when you change property names.

var results = [
    { "w1": "This", "w2": "is", "w3": "Big!" },
    { "w1": "Big", "w2": "as", "w3": "this?" }
];

msg.payload = results;

const output = [];

msg.payload.forEach((obj, index) => { // loop over payload array
    output[index] = {};
    Object.entries(obj).forEach(arr => { //loop over object key value pairs
        output[index][arr[0]] = {
            data: arr[1],
            hit:  arr[1].includes('h')
        }
    })
})
msg.payload  = output;

return msg;

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.