Header Filter UI Table for payload that keeps updating every second

Hi all,
Currently I have a UI Table with headerFilter feature to filter out rows. Yes it is working for data that static. However, if I have to udpate my ui table every second to display a counting time (note: data is selected from SQL table), the header filter is no longer functioning bcs it keeps refreshing every second also.

Hve been looking to these threads and it was good reference for static data but not for above issue.

This is my flow for reference. Any help would be appreciated. Thanks all!

[
    {
        "id": "c12357d81924ec43",
        "type": "tab",
        "label": "Flow 4",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "804e157652baac05",
        "type": "inject",
        "z": "c12357d81924ec43",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "1",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 330,
        "y": 360,
        "wires": [
            [
                "b932ddbc5407269b"
            ]
        ]
    },
    {
        "id": "b932ddbc5407269b",
        "type": "function",
        "z": "c12357d81924ec43",
        "name": "Select from DB",
        "func": "var array = flow.get ('array');\n\nif (array == undefined || array.length == 0)\n{\n    msg.payload = \"\";\n    return [msg,null];\n}\nelse\n{\n   \nconst output1 = array.map(({ Name }) => Name);\nconst name = \"(\" + output1.map(e => \"'\" + e + \"'\").join(\",\") + \")\"\n\nconst output2 = array.map(({ ID }) => ID);\nconst id = \"(\" + output2.map(e => \"'\" + e + \"'\").join(\",\") + \")\"\n\n\nmsg.payload = \"SELECT [Name], [ID], [Country], [Job], [Weight], [Status],[Queueing Time] FROM [dbo].[Data]  WHERE [Name]  IN \"+name+\" AND [ID] IN \"+id+\"  \" ;\nreturn [null,msg];\n}\n",
        "outputs": 2,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 540,
        "y": 360,
        "wires": [
            [
                "4582a0cbfb461040"
            ],
            [
                "73a23bb8b9168f4b"
            ]
        ]
    },
    {
        "id": "73a23bb8b9168f4b",
        "type": "MSSQL",
        "z": "c12357d81924ec43",
        "mssqlCN": "4f196903a82a4258",
        "name": "",
        "outField": "payload",
        "returnType": 0,
        "throwErrors": 1,
        "query": "",
        "modeOpt": "queryMode",
        "modeOptType": "query",
        "queryOpt": "payload",
        "queryOptType": "editor",
        "paramsOpt": "queryParams",
        "paramsOptType": "none",
        "rows": "rows",
        "rowsType": "msg",
        "params": [],
        "x": 740,
        "y": 400,
        "wires": [
            [
                "831b6ea232c8488c"
            ]
        ]
    },
    {
        "id": "831b6ea232c8488c",
        "type": "rbe",
        "z": "c12357d81924ec43",
        "name": "",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": true,
        "property": "payload",
        "topi": "topic",
        "x": 910,
        "y": 400,
        "wires": [
            [
                "4582a0cbfb461040"
            ]
        ]
    },
    {
        "id": "4582a0cbfb461040",
        "type": "function",
        "z": "c12357d81924ec43",
        "name": "Push to Table",
        "func": "if (msg.payload == null || msg.payload == \"\")\n{\n    var col = [];\n    msg.ui_control = \n{\n    \"tabulator\": {\n        \"columns\": col\n    }\n}\nreturn msg;\n}\n\nelse\n{\n    var key = Object.keys(msg.payload[0]);\n    flow.set ('object',key[0]);\n    var arr = [];\n    var col = [];\n\nfor(i = 0; i <key.length; i++){\n    arr = {\n        \"title\": key[i],\n        \"field\": key[i],\n        \"headerFilter\": \"input\"\n    }\n    col.push(arr)\n}\n\nmsg.ui_control = \n{\n    \"tabulator\": {\n        \"columns\": col\n    }\n}\nreturn msg;\n}\n\n\n\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1080,
        "y": 360,
        "wires": [
            [
                "0ad3cc426f36483c"
            ]
        ]
    },
    {
        "id": "0ad3cc426f36483c",
        "type": "ui_table",
        "z": "c12357d81924ec43",
        "group": "bc09249168d821ac",
        "name": "Washer New",
        "order": 1,
        "width": 30,
        "height": 5,
        "columns": [],
        "outputs": 1,
        "cts": true,
        "x": 1290,
        "y": 360,
        "wires": [
            []
        ]
    },
    {
        "id": "4f196903a82a4258",
        "type": "MSSQL-CN",
        "tdsVersion": "7_4",
        "name": "Test",
        "server": "LALU\\SQL2019",
        "port": "1433",
        "encyption": true,
        "trustServerCertificate": true,
        "database": "Test",
        "useUTC": true,
        "connectTimeout": "15000",
        "requestTimeout": "15000",
        "cancelTimeout": "5000",
        "pool": "5",
        "parseJSON": false,
        "enableArithAbort": true
    },
    {
        "id": "bc09249168d821ac",
        "type": "ui_group",
        "name": "Patient",
        "tab": "7dcc246f.ee661c",
        "order": 3,
        "disp": false,
        "width": "30",
        "collapse": false,
        "className": ""
    },
    {
        "id": "7dcc246f.ee661c",
        "type": "ui_tab",
        "name": "TEST",
        "icon": "dashboard",
        "order": 3,
        "disabled": false,
        "hidden": false
    }
]

Is there anyone have thought on this?

The filter node can not compare objects unless you convert them to strings.
You could convert the return names to a string and then compare if that string changes.
e.g.

[{"id":"b932ddbc5407269b","type":"function","z":"c12357d81924ec43","name":"Select from DB","func":"var array = flow.get ('array');\n\nif (array == undefined || array.length == 0)\n{\n    msg.payload = \"\";\n    return [msg,null];\n}\nelse\n{\n   \nconst output1 = array.map(({ Name }) => Name);\nconst name = \"(\" + output1.map(e => \"'\" + e + \"'\").join(\",\") + \")\"\n\nconst output2 = array.map(({ ID }) => ID);\nconst id = \"(\" + output2.map(e => \"'\" + e + \"'\").join(\",\") + \")\"\n\n\nmsg.payload = \"SELECT [Name], [ID], [Country], [Job], [Weight], [Status],[Queueing Time] FROM [dbo].[Data]  WHERE [Name]  IN \"+name+\" AND [ID] IN \"+id+\"  \" ;\nreturn [null,msg];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":360,"wires":[["4582a0cbfb461040"],["73a23bb8b9168f4b"]]},{"id":"804e157652baac05","type":"inject","z":"c12357d81924ec43","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":330,"y":360,"wires":[["b932ddbc5407269b"]]},{"id":"4582a0cbfb461040","type":"function","z":"c12357d81924ec43","name":"Push to Table","func":"if (msg.payload == null || msg.payload == \"\")\n{\n    var col = [];\n    msg.ui_control = \n{\n    \"tabulator\": {\n        \"columns\": col\n    }\n}\nreturn msg;\n}\n\nelse\n{\n    var key = Object.keys(msg.payload[0]);\n    flow.set ('object',key[0]);\n    var arr = [];\n    var col = [];\n\nfor(i = 0; i <key.length; i++){\n    arr = {\n        \"title\": key[i],\n        \"field\": key[i],\n        \"headerFilter\": \"input\"\n    }\n    col.push(arr)\n}\n\nmsg.ui_control = \n{\n    \"tabulator\": {\n        \"columns\": col\n    }\n}\nreturn msg;\n}\n\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1080,"y":360,"wires":[["0ad3cc426f36483c"]]},{"id":"73a23bb8b9168f4b","type":"MSSQL","z":"c12357d81924ec43","mssqlCN":"4f196903a82a4258","name":"","outField":"payload","returnType":0,"throwErrors":1,"query":"","modeOpt":"queryMode","modeOptType":"query","queryOpt":"payload","queryOptType":"editor","paramsOpt":"queryParams","paramsOptType":"none","rows":"rows","rowsType":"msg","params":[],"x":740,"y":400,"wires":[["285c527b5e6d5803"]]},{"id":"831b6ea232c8488c","type":"rbe","z":"c12357d81924ec43","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"fiilter","topi":"topic","x":930,"y":460,"wires":[["4582a0cbfb461040"]]},{"id":"0ad3cc426f36483c","type":"ui_table","z":"c12357d81924ec43","group":"bc09249168d821ac","name":"Washer New","order":1,"width":30,"height":5,"columns":[],"outputs":1,"cts":true,"x":1290,"y":360,"wires":[[]]},{"id":"285c527b5e6d5803","type":"change","z":"c12357d81924ec43","name":"","rules":[{"t":"set","p":"filter","pt":"msg","to":"$string($$.payload.Name)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":770,"y":460,"wires":[["831b6ea232c8488c"]]},{"id":"bc09249168d821ac","type":"ui_group","name":"Patient","tab":"7dcc246f.ee661c","order":3,"disp":false,"width":"30","collapse":false,"className":""},{"id":"7dcc246f.ee661c","type":"ui_tab","name":"TEST","icon":"dashboard","order":3,"disabled":false,"hidden":false}]

But it may be better to do a sql query for updated info from table then just add the changes. There is a sub flow from @Christian-Me that is useful for uit-table https://flows.nodered.org/flow/35d0480ce9151b2a722fa9d185a37825

[edit] or maybe slow down the update. 1 second is not really good as table could be constantly updateing every second, try maybe 15 seconds.

I don't think that is correct. The filter node will work with objects. I suspect it converts them to a json string if necessary.

Yes indeed, simple object. Maybe you have some idea what the issue is then?

You're not going to want to hear this, but you're going to want to ditch the table nodes and use a ui-template node to actually build Tabulator tables. Dashboard-ui-table was built on the Tabulator system that existed a couple years ago. It's been heavily modified since then. Also, the way ui-table was built, any time you redo the data, it completely resets and redraws the table. Not good if you're trying to do work while dynamically updating data in the table.

Using ui-template to build an actual Tabulator table seems daunting, but it's really easy. You just need two template nodes to do it. The first template node inserts the Tabulator source in the header of your dashboard page. Simply set your template to Add to site head section and insert the following code:

<link href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>

The second node is slightly trickier, but still very simple. It will consist of this:

<script>
    (some code)
    ...
</script>

Inside that script section is where you'll build your tabulator table. Navigate to tabulator.info and you'll find oodles of documentation and examples you can literally copy/paste from and have your table instantly work. All you need to do is know where your data is coming from and how to get it into your table.

Now to the reason why I said all that (after several weeks of beating my head against the wall with ui-table and finally ditching it for this method). Using Tabulator itself lends to two things. First, it's all recent code that is updated and works. Second, when you have dynamic data that keeps refreshing, raw Tabulator tables will just change the data where it sits and leave things like filters and whatnot alone. That's something ui-table just isn't built to do. So you can update data every second and have a running filter that keeps filtering it every time it's updated.

It's going to take some getting used to, but with the examples and documentation, you can do it.

Clicking the correct reply to ensure this gets flagged.

Thank you madhouse. This really helpful to direct me. Actually I was using ui template to build my table previously and changed to ui table due to simplicity.

If I use below code for the table, where should I put the headerFilter class/function to enable the filter on each column? or need a function bfore the template node?

<style>
    #history {
      font-family: "Arial";
        border-collapse: collapse;
        width: 100%;
        }
        
        #history td, #history th {
        border: 1px solid #ddd;
        padding: 8px;
        }
        #history tr:nth-child(even){background-color: #A8EEF8;}
        
        #history tr:hover {background-color: #40aeea;}
        
        #history th {
        padding-top: 12px;
        padding-bottom: 12px;
        text-align: center;
        background-color: #696969;
        color: white;
        }
        </style>
        
        <table id="history" border="1">
            <tr align="center">

                 <th>Name</th>
                 <th>ID</th>
                 <th>Country</th>
               
            </tr>
                <tbody>
                    <tr align="center" ng-repeat="row in msg.payload">
                        <td ng-repeat="item in row" >{{item}}</td>
                        </tr>
                        </tbody>
                        </table>
        

I want the filter something like this

And I dont think so that inside a template node, you can add copy/paste from tabulator.info bcs what I know tabulator.info is meant for ui-table node, not html table. Correct me if I'm wrong

So, here is what you say you want.


Here is the source code for it from the web page.

//custom max min header filter
var minMaxFilterEditor = function(cell, onRendered, success, cancel, editorParams){

    var end;

    var container = document.createElement("span");

    //create and style inputs
    var start = document.createElement("input");
    start.setAttribute("type", "number");
    start.setAttribute("placeholder", "Min");
    start.setAttribute("min", 0);
    start.setAttribute("max", 100);
    start.style.padding = "4px";
    start.style.width = "50%";
    start.style.boxSizing = "border-box";

    start.value = cell.getValue();

    function buildValues(){
        success({
            start:start.value,
            end:end.value,
        });
    }

    function keypress(e){
        if(e.keyCode == 13){
            buildValues();
        }

        if(e.keyCode == 27){
            cancel();
        }
    }

    end = start.cloneNode();
    end.setAttribute("placeholder", "Max");

    start.addEventListener("change", buildValues);
    start.addEventListener("blur", buildValues);
    start.addEventListener("keydown", keypress);

    end.addEventListener("change", buildValues);
    end.addEventListener("blur", buildValues);
    end.addEventListener("keydown", keypress);


    container.appendChild(start);
    container.appendChild(end);

    return container;
 }

//custom max min filter function
function minMaxFilterFunction(headerValue, rowValue, rowData, filterParams){
    //headerValue - the value of the header filter element
    //rowValue - the value of the column in this row
    //rowData - the data for the row being filtered
    //filterParams - params object passed to the headerFilterFuncParams property

        if(rowValue){
            if(headerValue.start != ""){
                if(headerValue.end != ""){
                    return rowValue >= headerValue.start && rowValue <= headerValue.end;
                }else{
                    return rowValue >= headerValue.start;
                }
            }else{
                if(headerValue.end != ""){
                    return rowValue <= headerValue.end;
                }
            }
        }

    return true; //must return a boolean, true if it passes the filter.
}


var table = new Tabulator("#example-table", {
    height:"311px",
    layout:"fitColumns",
    columns:[
        {title:"Name", field:"name", width:150, headerFilter:"input"},
        {title:"Progress", field:"progress", width:150, formatter:"progress", sorter:"number", headerFilter:minMaxFilterEditor, headerFilterFunc:minMaxFilterFunction, headerFilterLiveFilter:false},
        {title:"Gender", field:"gender", editor:"list", editorParams:{values:{"male":"Male", "female":"Female", clearable:true}}, headerFilter:true, headerFilterParams:{values:{"male":"Male", "female":"Female", "":""}, clearable:true}},
        {title:"Rating", field:"rating", editor:"star", hozAlign:"center", width:100, headerFilter:"number", headerFilterPlaceholder:"at least...", headerFilterFunc:">="},
        {title:"Favourite Color", field:"col", editor:"input", headerFilter:"list", headerFilterParams:{valuesLookup:true, clearable:true}},
        {title:"Date Of Birth", field:"dob", hozAlign:"center", sorter:"date",  headerFilter:"input"},
        {title:"Driver", field:"car", hozAlign:"center", formatter:"tickCross",  headerFilter:"tickCross",  headerFilterParams:{"tristate":true},headerFilterEmptyCheck:function(value){return value === null}},
    ],
});

Here is the resulting flow that I built with just copy/paste from the web page.

[{"id":"250290d53b07a92d","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"13c9bb7afaec28ee","type":"ui_template","z":"250290d53b07a92d","group":"e20c5b3c6a494e25","name":"","order":1,"width":0,"height":0,"format":"<link href=\"https://unpkg.com/tabulator-tables/dist/css/tabulator_site.min.css\" rel=\"stylesheet\">\n<script type=\"text/javascript\" src=\"https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js\"></script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","className":"","x":80,"y":40,"wires":[[]]},{"id":"5b1eaa2da8011171","type":"ui_template","z":"250290d53b07a92d","group":"e20c5b3c6a494e25","name":"","order":2,"width":0,"height":0,"format":"<div id=\"example-table\"></div>\n<script>\n    //sample data to be used in all tabulators\n    var tabledata = [\n    {id:1, name:\"Oli Bob\", progress:12, location:\"United Kingdom\", gender:\"male\", rating:1, col:\"red\", dob:\"14/04/1984\",\n    car:1, lucky_no:5, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:2, name:\"Mary May\", progress:1, location:\"Germany\", gender:\"female\", rating:2, col:\"blue\", dob:\"14/05/1982\",\n    car:true, lucky_no:10, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:3, name:\"Christine Lobowski\", progress:42, location:\"France\", gender:\"female\", rating:0, col:\"green\",\n    dob:\"22/05/1982\", car:\"true\", lucky_no:12, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:4, name:\"Brendon Philips\", progress:100, location:\"USA\", gender:\"male\", rating:1, col:\"orange\", dob:\"01/08/1980\",\n    car:false, lucky_no:18, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:5, name:\"Margret Marmajuke\", progress:16, location:\"Canada\", gender:\"female\", rating:5, col:\"yellow\",\n    dob:\"31/01/1999\", car:false, lucky_no:33, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:6, name:\"Frank Harbours\", progress:38, location:\"Russia\", gender:\"male\", rating:4, col:\"red\", dob:\"12/05/1966\",\n    car:1, lucky_no:2, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:7, name:\"Jamie Newhart\", progress:23, location:\"India\", gender:\"male\", rating:3, col:\"green\", dob:\"14/05/1985\",\n    car:true, lucky_no:63, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:8, name:\"Gemma Jane\", progress:60, location:\"China\", gender:\"female\", rating:0, col:\"red\", dob:\"22/05/1982\",\n    car:\"true\", lucky_no:72, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:9, name:\"Emily Sykes\", progress:42, location:\"South Korea\", gender:\"female\", rating:1, col:\"maroon\",\n    dob:\"11/11/1970\", car:false, lucky_no:44, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    {id:10, name:\"James Newman\", progress:73, location:\"Japan\", gender:\"male\", rating:5, col:\"red\", dob:\"22/03/1998\",\n    car:false, lucky_no:9, lorem:\"Lorem ipsum dolor sit amet, elit consectetur adipisicing \"},\n    ];\n    \n    var tabledatasimple = [\n    {id:1, name:\"Oli Bob\", location:\"United Kingdom\", gender:\"male\", rating:1, col:\"red\", dob:\"14/04/1984\"},\n    {id:2, name:\"Mary May\", location:\"Germany\", gender:\"female\", rating:2, col:\"blue\", dob:\"14/05/1982\"},\n    {id:3, name:\"Christine Lobowski\", location:\"France\", gender:\"female\", rating:0, col:\"green\", dob:\"22/05/1982\"},\n    {id:4, name:\"Brendon Philips\", location:\"USA\", gender:\"male\", rating:1, col:\"orange\", dob:\"01/08/1980\"},\n    {id:5, name:\"Margret Marmajuke\", location:\"Canada\", gender:\"female\", rating:5, col:\"yellow\", dob:\"31/01/1999\"},\n    {id:6, name:\"Frank Harbours\", location:\"Russia\", gender:\"male\", rating:4, col:\"red\", dob:\"12/05/1966\"},\n    {id:7, name:\"Jamie Newhart\", location:\"India\", gender:\"male\", rating:3, col:\"green\", dob:\"14/05/1985\"},\n    {id:8, name:\"Gemma Jane\", location:\"China\", gender:\"female\", rating:0, col:\"red\", dob:\"22/05/1982\"},\n    {id:9, name:\"Emily Sykes\", location:\"South Korea\", gender:\"female\", rating:1, col:\"maroon\", dob:\"11/11/1970\"},\n    {id:10, name:\"James Newman\", location:\"Japan\", gender:\"male\", rating:5, col:\"red\", dob:\"22/03/1998\"},\n    ];\n    \n    var tableDataNested = [\n    {id:1, name:\"Oli Bob\", location:\"United Kingdom\", gender:\"male\", rating:1, col:\"red\", dob:\"14/04/1984\", car:1,\n    lucky_no:5, _children:[\n    {id:2, name:\"Mary May\", progress:1, location:\"Germany\", gender:\"female\", rating:2, col:\"blue\", dob:\"14/05/1982\",\n    car:true, lucky_no:10},\n    {id:3, name:\"Christine Lobowski\", progress:42, location:\"France\", gender:\"female\", rating:0, col:\"green\",\n    dob:\"22/05/1982\", car:\"true\", lucky_no:12},\n    {id:4, name:\"Brendon Philips\", progress:100, location:\"USA\", gender:\"male\", rating:1, col:\"orange\", dob:\"01/08/1980\",\n    lucky_no:18, _children:[\n    {id:5, name:\"Margret Marmajuke\", progress:16, location:\"Canada\", gender:\"female\", rating:5, col:\"yellow\",\n    dob:\"31/01/1999\", lucky_no:33},\n    {id:6, name:\"Frank Harbours\", progress:38, location:\"Russia\", gender:\"male\", rating:4, col:\"red\", dob:\"12/05/1966\",\n    car:1, lucky_no:2},\n    ]},\n    ]},\n    {id:7, name:\"Jamie Newhart\", progress:23, location:\"India\", gender:\"male\", rating:3, col:\"green\", dob:\"14/05/1985\",\n    car:true, lucky_no:63},\n    {id:8, name:\"Gemma Jane\", progress:60, location:\"China\", gender:\"female\", rating:0, col:\"red\", dob:\"22/05/1982\",\n    car:\"true\", lucky_no:72, _children:[\n    {id:9, name:\"Emily Sykes\", progress:42, location:\"South Korea\", gender:\"female\", rating:1, col:\"maroon\",\n    dob:\"11/11/1970\", lucky_no:44},\n    ]},\n    {id:10, name:\"James Newman\", progress:73, location:\"Japan\", gender:\"male\", rating:5, col:\"red\", dob:\"22/03/1998\",\n    lucky_no:9},\n    ];\n    \n    var tabledatabig = [\n    {id:1, name:\"Oli Bob\", progress:12, gender:\"male\", rating:1, col:\"red\", dob:\"14/04/1984\", car:1, lucky_no:5},\n    {id:2, name:\"Mary May\", progress:1, gender:\"female\", rating:2, col:\"blue\", dob:\"14/05/1982\", car:true, lucky_no:10},\n    {id:3, name:\"Christine Lobowski\", progress:42, gender:\"female\", rating:0, col:\"green\", dob:\"22/05/1982\", car:\"true\",\n    lucky_no:12},\n    {id:4, name:\"Brendon Philips\", progress:100, gender:\"male\", rating:1, col:\"orange\", dob:\"01/08/1980\", lucky_no:18},\n    {id:5, name:\"Margret Marmajuke\", progress:16, gender:\"female\", rating:5, col:\"yellow\", dob:\"31/01/1999\", lucky_no:33},\n    {id:6, name:\"Frank Harbours\", progress:38, gender:\"male\", rating:4, col:\"red\", dob:\"12/05/1966\", car:1, lucky_no:2},\n    {id:7, name:\"Jamie Newhart\", progress:23, gender:\"male\", rating:3, col:\"green\", dob:\"14/05/1985\", car:true,\n    lucky_no:63},\n    {id:8, name:\"Gemma Jane\", progress:60, gender:\"female\", rating:0, col:\"red\", dob:\"22/05/1982\", car:\"true\", lucky_no:72},\n    {id:9, name:\"Emily Sykes\", progress:42, gender:\"female\", rating:1, col:\"maroon\", dob:\"11/11/1970\", lucky_no:44},\n    {id:10, name:\"James Newman\", progress:73, gender:\"male\", rating:5, col:\"red\", dob:\"22/03/1998\", lucky_no:9},\n    {id:11, name:\"Martin Barryman\", progress:20, gender:\"male\", rating:5, col:\"violet\", dob:\"04/04/2001\"},\n    {id:12, name:\"Jenny Green\", progress:56, gender:\"female\", rating:4, col:\"indigo\", dob:\"12/11/1998\", car:true},\n    {id:13, name:\"Alan Francis\", progress:90, gender:\"male\", rating:3, col:\"blue\", dob:\"07/08/1972\", car:true},\n    {id:14, name:\"John Phillips\", progress:80, gender:\"male\", rating:1, col:\"green\", dob:\"24/09/1950\", car:true},\n    {id:15, name:\"Ed White\", progress:70, gender:\"male\", rating:0, col:\"yellow\", dob:\"19/06/1976\"},\n    {id:16, name:\"Paul Branderson\", progress:60, gender:\"male\", rating:5, col:\"orange\", dob:\"01/01/1982\"},\n    {id:17, name:\"Gemma Jane\", progress:50, gender:\"female\", rating:2, col:\"red\", dob:\"14/04/1983\", car:true},\n    {id:18, name:\"Emma Netwon\", progress:40, gender:\"female\", rating:4, col:\"brown\", dob:\"07/10/1963\", car:true},\n    {id:19, name:\"Hannah Farnsworth\", progress:30, gender:\"female\", rating:1, col:\"pink\", dob:\"11/02/1991\"},\n    {id:20, name:\"Victoria Bath\", progress:20, gender:\"female\", rating:2, col:\"purple\", dob:\"22/03/1986\"},\n    ];\n\n    //custom max min header filter\nvar minMaxFilterEditor = function(cell, onRendered, success, cancel, editorParams){\n    \n    var end;\n    \n    var container = document.createElement(\"span\");\n    \n    //create and style inputs\n    var start = document.createElement(\"input\");\n    start.setAttribute(\"type\", \"number\");\n    start.setAttribute(\"placeholder\", \"Min\");\n    start.setAttribute(\"min\", 0);\n    start.setAttribute(\"max\", 100);\n    start.style.padding = \"4px\";\n    start.style.width = \"50%\";\n    start.style.boxSizing = \"border-box\";\n    \n    start.value = cell.getValue();\n    \n    function buildValues(){\n        success({\n        start:start.value,\n        end:end.value,\n        });\n    };\n\n    function keypress(e){\n        if(e.keyCode == 13){\n            buildValues();\n        }\n        \n        if(e.keyCode == 27){\n            cancel();\n        }\n    };\n    \n    end = start.cloneNode();\n    end.setAttribute(\"placeholder\", \"Max\");\n    \n    start.addEventListener(\"change\", buildValues);\n    start.addEventListener(\"blur\", buildValues);\n    start.addEventListener(\"keydown\", keypress);\n    \n    end.addEventListener(\"change\", buildValues);\n    end.addEventListener(\"blur\", buildValues);\n    end.addEventListener(\"keydown\", keypress);\n    \n    \n    container.appendChild(start);\n    container.appendChild(end);\n    \n    return container;\n}\n    \n    //custom max min filter function\nfunction minMaxFilterFunction(headerValue, rowValue, rowData, filterParams){\n    //headerValue - the value of the header filter element\n    //rowValue - the value of the column in this row\n    //rowData - the data for the row being filtered\n    //filterParams - params object passed to the headerFilterFuncParams property\n    \nif(rowValue){\n    if(headerValue.start != \"\"){\n        if(headerValue.end != \"\"){\n            return rowValue >= headerValue.start && rowValue <= headerValue.end; }else{ return rowValue>= headerValue.start;\n        }\n    }\n    else{\n        if(headerValue.end != \"\"){\n            return rowValue <= headerValue.end;}\n        }\n    }\n    return true; //must return a boolean, true if it passes the filter. \n} \nvar table=new Tabulator(\"#example-table\", {\n    height:\"311px\",\n    layout:\"fitColumns\",\n    data:tabledatabig,\n    columns:[\n        {title:\"Name\", field:\"name\", width:150, headerFilter:\"input\"},\n        {title:\"Progress\", field:\"progress\", width:150, formatter:\"progress\", sorter:\"number\", headerFilter:minMaxFilterEditor, headerFilterFunc:minMaxFilterFunction, headerFilterLiveFilter:false},\n        {title:\"Gender\", field:\"gender\", editor:\"list\", editorParams:{values:{\"male\":\"Male\", \"female\":\"Female\", clearable:true}}, headerFilter:true, headerFilterParams:{values:{\"male\":\"Male\", \"female\" :\"Female\", \"\" :\"\"}, clearable:true}},\n        {title:\"Rating\", field:\"rating\", editor:\"star\", hozAlign:\"center\", width:100, headerFilter:\"number\", headerFilterPlaceholder:\"at least...\", headerFilterFunc:\">=\"},\n        {title:\"Favourite Color\", field:\"col\", editor:\"input\", headerFilter:\"list\", headerFilterParams:{valuesLookup:true, clearable:true}},\n        {title:\"Date Of Birth\", field:\"dob\", hozAlign:\"center\", sorter:\"date\", headerFilter:\"input\"},\n        {title:\"Driver\", field:\"car\", hozAlign:\"center\", formatter:\"tickCross\", headerFilter:\"tickCross\", headerFilterParams:{\"tristate\":true},headerFilterEmptyCheck:function(value){return value === null}},\n    ],\n});\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":80,"y":80,"wires":[[]]},{"id":"e20c5b3c6a494e25","type":"ui_group","name":"Test","tab":"311b79f0d87bc5d7","order":9,"disp":true,"width":"30","collapse":false,"className":""},{"id":"311b79f0d87bc5d7","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]

Looks like this:
image
And produces this.


You can literally copy and paste the data from the Tabulator examples directly into the ui-template node and have it work. I changed only three things to make this table happen.

  1. Not really a necessary change, but I changed the header section from tabulator.min.css to tabulator_site.min.css to mimic the theme of the Tabulator site. Instructions on themes are on the site as well.
  2. While not visible in the source code for the table, the table data was pulled from the developers console while on the Tabulator page and pasted into the second ut-template. Hitting F12 pulls up the developers console and browsing through the elements will land you the variables used for the table data.
  3. The source code does not specify what data to use. I entered data:bigtabledata as one of the table setup options seen near the bottom of the source code in my flow.

The longest time I spent putting this together was trying to find a brace } that was hidden in a comment block due to how Node-Red pasted the code and not because of the code itself. The rest took less than five minutes total. Ui-table (library wrapper) was built to use Tabulator (source library) in Node-Red (IDE), not the other way around. Using Tabulator itself instead of the wrapper opens you up to the full functionality of Tabulator that the wrapper (ui-table) doesn't allow. And no, it's all Javascript based and not HTML based, though the Tabulator functionality is Javascript run inside HTML.

Hopefully that helps.

Thanks a lot. Really helpful. For the field part, if that have spaces between words, let say the field is Full Name (taken from SQL), the table wont show up. I tried to put

 field:"[Full Name]"

or

 field:["Full Name"]

or

 field:"[Full Name]"

Also not working

I know it's not recommended to have space on field name or SQL columns, but in case I have to face this situation.

I just tried to change the field name so it doesnt have spaces, but the outcome is the same (table not shown up). then, I want to check and tried to put inject node and function node that contains the object array of tabledatabig and change the data with msg.payload. The table also not showing up. It just works when data is inside the ui -template node. Maybe there's something wrong on how to push payload to the ui template node

var table=new Tabulator("#example-table", {
    height:"311px",
    layout:"fitColumns",
    data:msg.payload,
    columns:[
        {title:"Name", field:"name", width:150, headerFilter:"input"},
        {title:"Progress", field:"progress", width:150, formatter:"progress", sorter:"number", headerFilter:minMaxFilterEditor, headerFilterFunc:minMaxFilterFunction, headerFilterLiveFilter:false},
        {title:"Gender", field:"gender", editor:"list", editorParams:{values:{"male":"Male", "female":"Female", clearable:true}}, headerFilter:true, headerFilterParams:{values:{"male":"Male", "female" :"Female", "" :""}, clearable:true}},
        {title:"Rating", field:"rating", editor:"star", hozAlign:"center", width:100, headerFilter:"number", headerFilterPlaceholder:"at least...", headerFilterFunc:">="},
        {title:"Favourite Color", field:"col", editor:"input", headerFilter:"list", headerFilterParams:{valuesLookup:true, clearable:true}},
        {title:"Date Of Birth", field:"dob", hozAlign:"center", sorter:"date", headerFilter:"input"},
        {title:"Driver", field:"car", hozAlign:"center", formatter:"tickCross", headerFilter:"tickCross", headerFilterParams:{"tristate":true},headerFilterEmptyCheck:function(value){return value === null}},
    ],
});
</script>

Also on your flow, if you refresh the page, the table will disappear

This is a very simple fix. Don't use brackets. They're not supposed to get used. This is the actual name of the key you're looking for in the payload and spaces don't matter because Javascript uses JSON which essentially treats it as a string.

field:"Full Name"

Yes. This will require some reading beyond what is written in Tabulator because this is something you have to find more specifically for Node-Red. Implementing Tabulator in this way causes it to be asynchronous with the rest of the code. It loads according to the user on the web page, not according to the server. The code runs in the browser and reaches for the server whenever it needs to. When the table loads in the browser for the user, msg.payload most likely won't exist so no data. This needs an asynchronous way of handling it.

(function(scope) {

    scope.test = function(msg) {
        table.setData(msg.payload);
    }

})(scope)

Right now, I don't have access to how my tabulator deals with it, but you will put a block of code very similar to the above in your code somewhere. I will post the corrected code when I can get to it. But essentially, the code monitors for some kind of input to the ui-template node (which means the input will have to be tied to something that sends messages in Node-Red) and performs some kind of response when one arrives. That also means you completely remove your data:msg.payload line from your table setup. Your table will form with the column definitions and wait for data to be loaded in for it to display. Once it has data, it will display it.

This also brings up another point. You're pulling your data from an SQL database. You really shouldn't push the data to the table, but instead allow the table to pull the data by using an AJAX setup in the table and adding a HTTP in node in your flow connected to a function that pushes your query to your database and returns the result to the HTTP out node. Then the table can call it whenever it wants and load dynamically. I will leave you to research that on your own. It is very easy to use HTTP nodes and the AJAX setup is well documented on the website. It would be nice if Tabulator had a method of making an SQL query, but that has not been implemented. Likely because of everything that must go into it.

Yes. This is a part of how Javascript, HTML, your browser and Node-Red work. This is well beyond the scope of your question and would require a lot of in depth research to make it work properly. Short answer is the Dashboard created for Node-Red doesn't handle reloading Javascript sources nicely. There are some workarounds to get it to reload, but they're not very effective. If your Dashboard UI has multiple tabs, you can switch to a different tab and back which usually forces a reload and will display your table. Ui-control nodes also put out information on when a page is accessed or disconnected, which can be used to control some stuff. But the short answer is reload shouldn't be used and should be replaced in page handling with switching tabs or proper coding.

You will need to do a lot of reading in Tabulator to learn how to adjust your table code to fit what you're trying to load. The code I used to show the header filtering came straight from the examples and is setup to work with the example data. You're going to need to define your own columns from your own data, setup your own sources and add in the functionality you need for what you want it to do. That would take up several threads in this forum if the discussion kept going as it is. You've been seeded with everything you'll need to get started (once I correct my code for the msg.payload injection), so you should be good to go after some reading. Give it a try and if you get stuck on something that doesn't seem to be working, jump back on and we'll be glad to help. The nice thing is this stuff is pretty easy to manipulate and Javascript in general is well documented. I think you'll do just fine.

1 Like