Send msg.payload from ui template to nodered

hi all, I have a ui template with some javascript function
i would like return a value to node-red

<script type="text/javascript">
        function onQRCodeScanned(scannedText)
        {   
            var scannedTextMemo = document.getElementById("scannedTextMemo");
            if(scannedTextMemo)
            {
                console.log({payload: "test" + scannedText});

            }
</script>

How can I send scannedText to nodered?
many thanks

can someone help me?

The built in help has an example of how to send data back from UI template to node-red.

Also, on the forum, search scope send in the Dashboard category.

Here is one such example: Upload file to SQL table - #4 by Steve-Mcl - check out the ui_template named "Upload Button"


Alternatively, you could create a http endpoint flow to receive data & use Ajax in your template to POST data back to node-red.

i'm really sorry for disturb
I'm trying using many example but i have one small problem

i have written this function

    <script>
        function scannedTransfer(){
            var UUID = ScannedUUID;
            //added some output for testing
            console.log("Working, Scanned: " + UUID)
            alert(UUID);
            this.send(UUID);
        }
    </script>

if function is called the console.log and the alert works very well while the this.send not works :frowning:

Which dashboard is this? Original (old v1) or new flowfuse dashboard 2.0?

i'm using 1

In which case, this.send(UUID) will not work. Read the built in help. You need to use the scope object.

Hi,
I have the same issue, nothing coming out of the template node:
I can see the 'Here!' in the console but noting in the debug node

<script>
    (function(scope) {
        scope.$watch('msg', function(msg) {
        if (msg) {
            console.log("here!");
            scope.send({payload: "test_send"});
        }
        });
    })(scope);

</script>

No, your code is very different. You should probably start your own thread and provide a minimal flow that demonstrates your issue.

Do you have a debug attached to the output of the template?

Also, why have you placed that inside the watch msg handler? This means you need to send a message to generate a msg, which is a bit pointless as it is.

Typically, you create a function that is called by a button.

You maybe right, I will move to a new thread

im really sorry for disturb you again

i try in this way:

    <script type="text/javascript">
        var ScannedUUID="ciao";
        function onQRCodeScanned(scannedText)
        {   
            var scannedTextMemo = document.getElementById("scannedTextMemo");
            if(scannedTextMemo)
            {
                
                scannedTextMemo.value = scannedText;
                scannedTransfer(scannedText)
                console.log({payload: scannedText});
            }
        }

(function(scope) {
            scope.scannedTransfer = function(payload) {
                let msg = {
                    payload
                }
                scope.send(msg);
            };
        })(scope);
</script>

nothing output from scope funcion
i'm sorry again

Where is onQRCodeScanned called?

You cannot call scannedTransfer outside of scope


Try adding console.log("inside scope") console.log("inside scope scannedTransfer") statements outside and inside of your functions. Look in the browser console for them. Click the code link and add debug points to understand what is happening.

many many thanks for your available
to better understand the problem I attach entire code

<!doctype html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link type="text/css" rel="stylesheet" href="/static/JsQRScanner.css">
    <script type="text/javascript" src="/static/js/jsqrscanner.nocache.js"></script>
</head>
<body>
    <div class="row-element-set row-element-set-QRScanner">
        <h1>JsQRScanner</h1>
        <div class="row-element">
            <div class="FlexPanel detailsPanel QRScannerShort">
                <div class="FlexPanel shortInfoPanel">
                    <div class="gwt-HTML">
                        Point the webcam to a QR code.
                    </div>
                </div>
            </div>
        </div>
        <br>
        <div class="row-element">
            <div class="qrscanner" id="scanner">
            </div>
        </div>
        <div class="row-element">
            <div class="form-field form-field-memo">
                <div class="form-field-caption-panel">
                    <div class="gwt-Label form-field-caption">
                        Scanned text
                    </div>
                </div>
                <div class="FlexPanel form-field-input-panel">
                    <textarea id="scannedTextMemo" class="textInput form-memo form-field-input textInput-readonly" rows="3" readonly>
                    </textarea>
                </div>
            </div>
        </div>
        <br> 
    </div>

    <script type="text/javascript">
        function onQRCodeScanned(scannedText)
        {   
            var scannedTextMemo = document.getElementById("scannedTextMemo");
            if(scannedTextMemo)
            {    
                scannedTextMemo.value = scannedText;
                console.log({payload: scannedText});
                //HERE NEED CALL scope.send in order to send to node-red the scannedText value
            }
        }

        function provideVideo()
        {
            var n = navigator;
            if (n.mediaDevices && n.mediaDevices.getUserMedia)
            {
            return n.mediaDevices.getUserMedia({
                video: {
                facingMode: "environment"
                },
                audio: false
            });
            }    
            return Promise.reject('Your browser does not support getUserMedia');
        }

        function provideVideoQQ()
        {
            return navigator.mediaDevices.enumerateDevices()
            .then(function(devices) {
                var exCameras = [];
                devices.forEach(function(device) {
                if (device.kind === 'videoinput') {
                exCameras.push(device.deviceId)
                }
            });  
                return Promise.resolve(exCameras);
            }).then(function(ids){
                if(ids.length === 0)
                {
                return Promise.reject('Could not find a webcam');
                }
                
                return navigator.mediaDevices.getUserMedia({
                    video: {
                    'optional': [{
                        'sourceId': ids.length === 1 ? ids[0] : ids[1]//this way QQ browser opens the rear camera
                        }]
                    }
                });        
            });                
        }

        function JsQRScannerReady()
        { 
            var jbScanner = new JsQRScanner(onQRCodeScanned);
            jbScanner.setSnapImageMaxSize(300);
            var scannerParentElement = document.getElementById("scanner");
            if(scannerParentElement)
            {
                jbScanner.appendTo(scannerParentElement);
            }        
        }

        (function(scope) {
            scope.scannedTransfer = function(input) {
                let msg = {
                    payload : input
                }
                scope.send(msg);
            };
        })(scope);
    </script>
</body>
</html>

or maybe is easies to call scope function from textarea with event onchange? I prefer using javascript only if you can help me
many many thanks in advance

Where is the HTML placed in your flow? what node? how do you serve this?

Probably better to share a flow export (only select the nodes that matter - i.e. minimal possible reproducible flow)

many many thanks I have a project with ui template and debug nodes only

[
    {
        "id": "906edfc6d47a2107",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "96206c0bbcc7557d",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "navigationStyle": "default"
    },
    {
        "id": "b72f4bc82dfbbef4",
        "type": "ui-theme",
        "name": "Default Theme",
        "colors": {
            "surface": "#ffffff",
            "primary": "#0094CE",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    },
    {
        "id": "b20ed9dfbe5e854b",
        "type": "ui-page",
        "name": "Page",
        "ui": "96206c0bbcc7557d",
        "path": "/page",
        "icon": "home",
        "layout": "grid",
        "theme": "b72f4bc82dfbbef4",
        "order": -1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "2166414fd2f5f24e",
        "type": "ui-group",
        "name": "My Group",
        "page": "b20ed9dfbe5e854b",
        "width": "6",
        "height": "1",
        "order": -1,
        "showTitle": true,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "dbf6f95082c0e8cc",
        "type": "ui_tab",
        "name": "Home",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    },
    {
        "id": "fb010ea46d95b0cb",
        "type": "ui_base",
        "theme": {
            "name": "theme-light",
            "lightTheme": {
                "default": "#0094CE",
                "baseColor": "#0094CE",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": false
            },
            "darkTheme": {
                "default": "#097479",
                "baseColor": "#097479",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": false
            },
            "customTheme": {
                "name": "Untitled Theme 1",
                "default": "#4B7930",
                "baseColor": "#4B7930",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
            },
            "themeState": {
                "base-color": {
                    "default": "#0094CE",
                    "value": "#0094CE",
                    "edited": false
                },
                "page-titlebar-backgroundColor": {
                    "value": "#0094CE",
                    "edited": false
                },
                "page-backgroundColor": {
                    "value": "#fafafa",
                    "edited": false
                },
                "page-sidebar-backgroundColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "group-textColor": {
                    "value": "#1bbfff",
                    "edited": false
                },
                "group-borderColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "group-backgroundColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "widget-textColor": {
                    "value": "#111111",
                    "edited": false
                },
                "widget-backgroundColor": {
                    "value": "#0094ce",
                    "edited": false
                },
                "widget-borderColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "base-font": {
                    "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
                }
            },
            "angularTheme": {
                "primary": "indigo",
                "accents": "blue",
                "warn": "red",
                "background": "grey",
                "palette": "light"
            }
        },
        "site": {
            "name": "Node-RED Dashboard",
            "hideToolbar": "false",
            "allowSwipe": "false",
            "lockMenu": "false",
            "allowTempTheme": "true",
            "dateFormat": "DD/MM/YYYY",
            "sizes": {
                "sx": 48,
                "sy": 48,
                "gx": 6,
                "gy": 6,
                "cx": 6,
                "cy": 6,
                "px": 0,
                "py": 0
            }
        }
    },
    {
        "id": "ca12a15e938714e5",
        "type": "ui_group",
        "name": "Default",
        "tab": "dbf6f95082c0e8cc",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "51bf0475.ddfd2c",
        "type": "ui_group",
        "name": "Group 2",
        "tab": "279d8078.dcf29",
        "order": 2,
        "disp": true,
        "width": "6"
    },
    {
        "id": "279d8078.dcf29",
        "type": "ui_tab",
        "z": "906edfc6d47a2107",
        "name": "New Test",
        "icon": "dashboard"
    },
    {
        "id": "81372c32f0628efa",
        "type": "debug",
        "z": "906edfc6d47a2107",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 580,
        "y": 320,
        "wires": []
    },
    {
        "id": "489b75fd90578069",
        "type": "ui_template",
        "z": "906edfc6d47a2107",
        "group": "ca12a15e938714e5",
        "name": "",
        "order": 0,
        "width": 0,
        "height": 0,
        "format": "<!doctype html>\n<html>\n<head>\n    <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"/static/JsQRScanner.css\">\n    <script type=\"text/javascript\" src=\"/static/js/jsqrscanner.nocache.js\"></script>\n</head>\n<body>\n    <div class=\"row-element-set row-element-set-QRScanner\">\n        <h1>JsQRScanner</h1>\n        <div class=\"row-element\">\n            <div class=\"FlexPanel detailsPanel QRScannerShort\">\n                <div class=\"FlexPanel shortInfoPanel\">\n                    <div class=\"gwt-HTML\">\n                        Point the webcam to a QR code.\n                    </div>\n                </div>\n            </div>\n        </div>\n        <br>\n        <div class=\"row-element\">\n            <div class=\"qrscanner\" id=\"scanner\">\n            </div>\n        </div>\n        <div class=\"row-element\">\n            <div class=\"form-field form-field-memo\">\n                <div class=\"form-field-caption-panel\">\n                    <div class=\"gwt-Label form-field-caption\">\n                        Scanned text\n                    </div>\n                </div>\n                <div class=\"FlexPanel form-field-input-panel\">\n                    <textarea id=\"scannedTextMemo\" onchange=\"scannedTransfer()\" class=\"textInput form-memo form-field-input textInput-readonly\" rows=\"3\" readonly>\n                    </textarea>\n                </div>\n            </div>\n        </div>\n        <br> \n    </div>\n\n    <script type=\"text/javascript\">\n    \n        function onQRCodeScanned(scannedText)\n        {   \n            var scannedTextMemo = document.getElementById(\"scannedTextMemo\");\n            if(scannedTextMemo)\n            {    \n                scannedTextMemo.value = scannedText;\n                console.log({payload: scannedText});\n                //HERE NEED CALL scope.send in order to send to node-red the scannedText value\n            }\n        }\n\n        function provideVideo()\n        {\n            var n = navigator;\n            if (n.mediaDevices && n.mediaDevices.getUserMedia)\n            {\n            return n.mediaDevices.getUserMedia({\n                video: {\n                facingMode: \"environment\"\n                },\n                audio: false\n            });\n            }    \n            return Promise.reject('Your browser does not support getUserMedia');\n        }\n\n        function provideVideoQQ()\n        {\n            return navigator.mediaDevices.enumerateDevices()\n            .then(function(devices) {\n                var exCameras = [];\n                devices.forEach(function(device) {\n                if (device.kind === 'videoinput') {\n                exCameras.push(device.deviceId)\n                }\n            });  \n                return Promise.resolve(exCameras);\n            }).then(function(ids){\n                if(ids.length === 0)\n                {\n                return Promise.reject('Could not find a webcam');\n                }\n                \n                return navigator.mediaDevices.getUserMedia({\n                    video: {\n                    'optional': [{\n                        'sourceId': ids.length === 1 ? ids[0] : ids[1]//this way QQ browser opens the rear camera\n                        }]\n                    }\n                });        \n            });                \n        }\n\n        function JsQRScannerReady()\n        { \n            var jbScanner = new JsQRScanner(onQRCodeScanned);\n            jbScanner.setSnapImageMaxSize(300);\n            var scannerParentElement = document.getElementById(\"scanner\");\n            if(scannerParentElement)\n            {\n                jbScanner.appendTo(scannerParentElement);\n            }        \n        }\n\n        (function(scope) {\n            var scannedTextMemo = document.getElementById(\"scannedTextMemo\");\n            $('scannedTextMemo.value').on('change',function(){\n                console.log('ciao');\n                scope.send(scannedTextMemo.value);\n            });\n            scope.scannedTransfer = function(input) {\n                let msg = {\n                    payload : input\n                }\n                scope.send(msg);\n            };\n        })(scope);\n    </script>\n</body>\n</html>\n",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 580,
        "y": 220,
        "wires": [
            [
                "81372c32f0628efa"
            ]
        ]
    }
]

firsly, node-red-dasboard creates the whole HTML + BODY - so you should NOT include thos in your template.

Next, in order to access scope you need your functions to be in side that first function

What you had

  function onQRCodeScanned(){
    // can NOT use scope here
  }
 (function (scope) {
    // can use scope here
  })()

What you should have


 (function (scope) {
    // can use scope here
      function onQRCodeScanned(){
        // can use scope here
      }
  })()

Here is something to try (100% UNTESTED)

<div class="row-element-set row-element-set-QRScanner">
    <h1>JsQRScanner</h1>
    <div class="row-element">
        <div class="FlexPanel detailsPanel QRScannerShort">
            <div class="FlexPanel shortInfoPanel">
                <div class="gwt-HTML">
                    Point the webcam to a QR code.
                </div>
            </div>
        </div>
    </div>
    <br>
    <div class="row-element">
        <div class="qrscanner" id="scanner">
        </div>
    </div>
    <div class="row-element">
        <div class="form-field form-field-memo">
            <div class="form-field-caption-panel">
                <div class="gwt-Label form-field-caption">
                    Scanned text
                </div>
            </div>
            <div class="FlexPanel form-field-input-panel">
                <textarea id="scannedTextMemo" class="textInput form-memo form-field-input textInput-readonly" rows="3"
                    readonly>
                </textarea>
            </div>
        </div>
    </div>
    <br>
</div>

<script type="text/javascript">
    (function (scope) {
        function onQRCodeScanned(scannedText) {
            var scannedTextMemo = document.getElementById("scannedTextMemo");
            if (scannedTextMemo) {
                scannedTextMemo.value = scannedText;
                console.log({ payload: scannedText });
                scope.send({ payload: scannedText });
            }
        }

        function provideVideo() {
            var n = navigator;
            if (n.mediaDevices && n.mediaDevices.getUserMedia) {
                return n.mediaDevices.getUserMedia({
                    video: {
                        facingMode: "environment"
                    },
                    audio: false
                });
            }
            return Promise.reject('Your browser does not support getUserMedia');
        }

        function provideVideoQQ() {
            return navigator.mediaDevices.enumerateDevices()
                .then(function (devices) {
                    var exCameras = [];
                    devices.forEach(function (device) {
                        if (device.kind === 'videoinput') {
                            exCameras.push(device.deviceId)
                        }
                    });
                    return Promise.resolve(exCameras);
                }).then(function (ids) {
                    if (ids.length === 0) {
                        return Promise.reject('Could not find a webcam');
                    }

                    return navigator.mediaDevices.getUserMedia({
                        video: {
                            'optional': [{
                                'sourceId': ids.length === 1 ? ids[0] : ids[1]//this way QQ browser opens the rear camera
                            }]
                        }
                    });
                });
        }

        function JsQRScannerReady() {
            var jbScanner = new JsQRScanner(onQRCodeScanned);
            jbScanner.setSnapImageMaxSize(300);
            var scannerParentElement = document.getElementById("scanner");
            if (scannerParentElement) {
                jbScanner.appendTo(scannerParentElement);
            }
        }

    })(scope);
</script>

many thanks

I did some test:

  1. if I put all functions inside (function(node){... seems that the package call in the line

not works and the function are not called.

If I try in the following way:

<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link type="text/css" rel="stylesheet" href="/static/JsQRScanner.css">
<script type="text/javascript" src="/static/js/jsqrscanner.nocache.js"></script>
<div class="row-element-set row-element-set-QRScanner">
    <h1>JsQRScanner</h1>
    <div class="row-element">
        <div class="FlexPanel detailsPanel QRScannerShort">
            <div class="FlexPanel shortInfoPanel">
                <div class="gwt-HTML">
                    Point the webcam to a QR code.
                </div>
            </div>
        </div>
    </div>
    <br>
    <div class="row-element">
        <div class="qrscanner" id="scanner">
        </div>
    </div>
    <div class="row-element">
        <div class="form-field form-field-memo">
            <div class="form-field-caption-panel">
                <div class="gwt-Label form-field-caption">
                    Scanned text
                </div>
            </div>
            <div class="FlexPanel form-field-input-panel">
                <textarea id="scannedTextMemo" class="textInput form-memo form-field-input textInput-readonly" rows="3" readonly>
                </textarea>
            </div>
        </div>
    </div>
    <br> 
    <md-button ng-click="myfunction('OK')">ok</md-button>
</div>


<script type="text/javascript">
    (function(scope) {
       **//WORKS ONLY IF I PUSH BUTTON**
        scope.myfunction = function(payload) { 
            var scannedTextMemo = document.getElementById("scannedTextMemo");
            console.log(scannedTextMemo.value)
            let msg = {
                payload,
                topic:scannedTextMemo.value
            }   
            scope.send(msg);
        };
    })(scope);

    scope.send({payload: "preload"}); **//WORKS outside previous scope function too**

    function onQRCodeScanned(scannedText,scope,payload)
    {   
        var scannedTextMemo = document.getElementById("scannedTextMemo");
        if(scannedTextMemo)
        {    
            scannedTextMemo.value = scannedText;
            console.log({payload: scannedText});
            scope.send({payload: "load"}); **//NOT WORKS NEVER**
        }
    }

    function provideVideo()
    {
        var n = navigator;
        if (n.mediaDevices && n.mediaDevices.getUserMedia)
        {
        return n.mediaDevices.getUserMedia({
            video: {
            facingMode: "environment"
            },
            audio: false
        });
        }    
        return Promise.reject('Your browser does not support getUserMedia');
    }

    function provideVideoQQ()
    {
        return navigator.mediaDevices.enumerateDevices()
        .then(function(devices) {
            var exCameras = [];
            devices.forEach(function(device) {
            if (device.kind === 'videoinput') {
            exCameras.push(device.deviceId)
            }
        });  
            return Promise.resolve(exCameras);
        }).then(function(ids){
            if(ids.length === 0)
            {
            return Promise.reject('Could not find a webcam');
            }
            
            return navigator.mediaDevices.getUserMedia({
                video: {
                'optional': [{
                    'sourceId': ids.length === 1 ? ids[0] : ids[1]//this way QQ browser opens the rear camera
                    }]
                }
            });        
        });                
    }

    function JsQRScannerReady()
    { 
        var jbScanner = new JsQRScanner(onQRCodeScanned);
        jbScanner.setSnapImageMaxSize(300);
        var scannerParentElement = document.getElementById("scanner");
        if(scannerParentElement)
        {
            jbScanner.appendTo(scannerParentElement);
        }        
    }
    scope.send({payload:"postLoad"}) **//WORKS outside previous scope function too**
</script>

  1. As I commented some scope.send() works outside (function(scope){
  2. the scope.send inside the scope function works when I push the button which I added just for a test
  3. the scope.send inside the function onQRCodeScanned never works

I tried put all code inside the (function(scope{ ... }(scope) but the external javascript code is not loaded

maybe I have some problem to understand how to use the scope function