DB UI template problem when used from remote browser

Hi all ,

In one of my flow, I use a UI template node, which is used for several things :

  • handle JS tabulator features , this part works well, either from a browser running on the same computer as NR, or from a remote browser.

  • copy content of a cvs passed as msg.data to the OS clipboard , using this piece of code :

            case "copytoclipboard":

                //$scope.send({payload: "this is copyclipboard"});
                // Copy the text inside the text field
                navigator.clipboard.writeText(msg.data);
                
                // Alert the copied text
                alert("Copied the text: " + msg.data);
                
                break;

This works perfectly when using in the local browser ( same computer as the one running Node Red), but not from another browser over the network, I get the ierror in browser console :

image

Does anyone have any idea about where this problem come from ?

For reference if helpful, here is the full code of the UI template node :

<form>
    <div id="gridExample" style="background-color:#000000;margin:0px;border:0px solid black"></div>
</form>

<script type="text/javascript">
    var $scope = this.scope;

var grid = new Tabulator("#gridExample", {
    rowFormatter : function(row){
        // chnage row color based on mainDstIp specific value 
        if(row.getData()["vertexType"].includes("V")){
            row.getElement().style.backgroundColor = "#b3ffb3";
            row.getElement().style.color = "black";
            }
        if(row.getData()["vertexType"].includes("A")){
            row.getElement().style.backgroundColor = "#ffb3cc";
            row.getElement().style.color = "black";
            }
        if(row.getData()["vertexType"].includes("D")){
            row.getElement().style.backgroundColor = "#b3daff";
            row.getElement().style.color = "black";
            }
},
    layout:"fitColumns", //fitColumns
    movableColumns: true,
    headerVisible: true,
    resizableColumns: true,
    //selectable: 5,
    //responsiveLayout: "collapse",
    //autoResize: true,
    pagination: "true",
    paginationSizeSelector:[10, 25, 50, 100, true], //select list with an "all" option at the end of the list
    height: "800px",
    groupBy:["rxTx", "control"],
    groupToggleElement:"header", //toggle group on click anywhere in the group header
    //  groupStartOpen: true,
    
    data:[
    ],
    
    columns:[/*
              {formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", headerSort:false, cellClick:function(e, cell){
         cell.getRow().toggleSelect();
    }}*/
    {
    formatter:"rowSelection",
    titleFormatter:"rowSelection",
    titleFormatterParams:{
        rowRange:"active"
        }, //only toggle the values of the active filtered rows
        hozAlign:"center", 
        headerSort:false, 
        cellClick:function(e, cell){
            cell.getRow().toggleSelect();
    }},
            //{title:"ID", field:"#",   resizable: true, formatter:"textarea", width:200},
            {title:"Factory label", field:"Factory label", resizable: true, formatter:"textarea", width:200},
                        {title:"Rx/Tx", 
                        field:"rxTx", 
                        resizable: true,  
                        editor:"input", 
                        headerFilter:true, //show header filter matching the cells editor, 
                        width:100},
    
            {title:"chNumber", 
            field:"chNumber", 
            resizable: true,  
            editor:"input", 
            headerFilter:true, //show header filter matching the cells editor, 
            width:100},
            {title:"tags", field:"tags", resizable: true, width:250},
            {title:"descriptor", field:"descriptor", resizable: true, width:300},
            {title:"control", field:"control",     resizable: true, width:100},
            {title:"useAsEndpoint", field:"useAsEndpoint", formatter:"tickCross", resizable: true, width:100},
            {title:"sdpSupport", field:"sdpSupport", formatter:"tickCross",resizable: true, width:120},
            {title:"sipsMode", field:"sipsMode", resizable: true, width:100},
            {title:"active", field:"active",formatter:"tickCross", resizable: true, width:100},

            {title:"mainDstIp", field:"mainDstIp", resizable: true, width:100},
            //{title:"mainDstPort", field:"mainDstPort", resizable: true, width:100},
            {title:"spareDstIp", field:"spareDstIp", resizable: true, width:100}
            //{title:"spareDstPort", field:"spareDstPort", resizable: true, width:100}

      ]
    });

    grid.on("tableBuilt", function(){
        $scope.send({payload: "Grid is ready"});
    });


grid.on("dataFiltered", function(filters, rows){
console.log(`Post-filter event: ${rows.length} rows`);
let data = [];
for (let i = 0 ; i < rows.length; i++) 
data.push(rows[i]._row.data);
var msg = {};
msg.payload = data;
msg.topic = "filtered_content";
msg.length = rows.length;
$scope.send(msg);
//$scope.send({topic:"filtered_content"},{payload: "export" + data}); 
});
/*
grid.on("dataFiltered", function(filters, rows){
        //filters - array of filters currently applied
        //rows - array of row components that pass the filters
        console.log(`Post-filter event: ${rows.length} rows`);
        for (let i = 0 ; i < rows.length; i++)
            console.log(Object.entries(rows[i]));
    });
*/
    grid.on("rowClick", function(e, row){
    //alert("Row " + row.getData().descriptor + " Clicked!!!!");
    $scope.send({payload: row.getData().descriptor + " Clicked!!!!"});
    });

(function(scope) {
    $scope.$watch('msg', function(msg)
    {
        if (msg)
        switch (msg.payload)
        {
            case "setdata":
                //grid.clearData();
                grid.setData(msg.data);
                $scope.send({payload: "Grid updated"});
                break;

            case "copytoclipboard":

                navigator.clipboard.writeText(msg.data);

                alert("Copied the text: " + msg.data);
                
                break;

        }
    });
})(scope);
</script>

Thanks in advance,

Jerome

I think this is related to the Clipboard API having restrictions.

Such as requiring SSL if not localhost - you might need to rethink your approach, or use SSL on your dashboard

Clipboard: writeText() method - Web APIs | MDN (mozilla.org)

Hi,

thanks for your reply, I'll dig into it.

Jerome

Indeed it seems directly related to the SSL thing.

We'll check to switch dashboard to https.

Thanks for bringing us on the right path.

1 Like

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