Problem sending message from ui-template node

I created a ui-template node for my dashboard containing some code to create a clock with a clickable Home icon on the upper-right corner of my dashboard. The node is being added to the site header. The clock and Home icons display just fine but I"m having trouble making the home icon send you back to the homepage when clicked. In my code, I am able to fire off an event when the icon is clicked, but I get an error saying that send({payload:"Home"}) function is not defined. here's the code for the template node below:

<script type="text/javascript">
    var theScope = null;

    $(function(scope)
    {
        theScope = scope;
        if($('.md-toolbar-tools').length != 0)
            loadClock();
        else 
            setTimeout(loadClock, 500);
    });
    
    function loadClock(){
        $('#clock').remove();
        var toolbar = $('.md-toolbar-tools');
        
        var div = $('<div/ class="clock-style">');
        var p = $('<p/ id="clock">');
        
        div.append(p);
        div[0].style.margin = '5px 5px 5px auto';
        toolbar.append(div);
        
        // Add element for clickable home icon if not already present
        var i = document.createElement('i');
        i.classList.add("fa");
        i.classList.add("fa-home");
        i.classList.add("fa-2x");
        i.classList.add("icon-padding");
        i.addEventListener('click', sendPayload);
        toolbar.append(i);
        
        function displayTitle(lh) {
            p.text(lh); 
        }
        
        function upTime() {
            var d = new Date();
            p.text(d.toLocaleTimeString([], {hour: 'numeric', minute:'2-digit'}));
        }
    
        if(document.clockInterval){ 
            clearInterval(document.clockInterval);
            document.clockInterval = null;
        }
            
        document.clockInterval = setInterval(upTime, 1000);
    
        function sendPayload(){
            theScope.send({payload:"Home"});
        }
    }
</script>

Any idea what I'm doing wrong? I just want to send a message to the UI node to change the dashboard page.

NOTE: Above code edited to include missing assignment statement for the theScope variable inside of the main function.

Hello ..

Why do you set the theScope to null ?
set it to var theScope = scope;
so you can have access to the send method

Actually, the original rendition of this code had theScope = scope; at the top. But when running the dashboard page, I get an error saying 'scope is not defined.' So after looking around at some other examples, I noticed that some showed the scope variable being passed in from the anonymous main function that gets called when the script first runs, so I included it there and assigned theScope = scope; inside this function (By the way, I forgot to include that change in the code I posted earlier. It is included now).

But this time, I get an error saying, "theScope.send is not a function"

Use the self calling Function as the example in the Help tab of the ui_template
that passes scope so it can be accessed anywhere in the code.
then use it as scope.send({ payload: "Home" });

ui_template Code
<script type="text/javascript">

    (function (scope) {


        if ($('.md-toolbar-tools').length != 0)
            loadClock();
        else
            setTimeout(loadClock, 500);


        function loadClock() {
            $('#clock').remove();
            var toolbar = $('.md-toolbar-tools');

            var div = $('<div/ class="clock-style">');
            var p = $('<p/ id="clock">');

            div.append(p);
            div[0].style.margin = '5px 5px 5px auto';
            toolbar.append(div);

            // Add element for clickable home icon if not already present
            var i = document.createElement('i');
            i.classList.add("fa");
            i.classList.add("fa-home");
            i.classList.add("fa-2x");
            i.classList.add("icon-padding");
            i.addEventListener('click', sendPayload);
            toolbar.append(i);

            function displayTitle(lh) {
                p.text(lh);
            }

            function upTime() {
                var d = new Date();
                p.text(d.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }));
            }

            if (document.clockInterval) {
                clearInterval(document.clockInterval);
                document.clockInterval = null;
            }

            document.clockInterval = setInterval(upTime, 1000);

            function sendPayload() {
                scope.send({ payload: "Home" });
            }
        }


    })(scope);




</script>

Hello, I'm still struggling to get this to work (no idea what I'm still doing wrong). Here's the latest code I have been testing

[{"id":"0c14a683067f1149","type":"ui_template","z":"a084448f7f5e471e","group":"c982fbb8.1deb38","name":"Global Styles","order":2,"width":"0","height":"0","format":"<script type=\"text/javascript\">\n    $(function(scope)\n    {\n        if($('.md-toolbar-tools').length != 0)\n            loadClock();\n        else \n            setTimeout(loadClock, 500);\n        \n        function sendPayload(){\n            scope.send({payload:\"Home\"});\n        }\n    \n        function loadClock(){\n            $('#clock').remove();\n            var toolbar = $('.md-toolbar-tools');\n            \n            var div = $('<div/ class=\"clock-style\">');\n            var p = $('<p/ id=\"clock\">');\n            \n            div.append(p);\n            div[0].style.margin = '5px 5px 5px auto';\n            toolbar.append(div);\n            \n            // Add element for clickable home icon if not already present\n            var i = document.createElement('i');\n            i.classList.add(\"fa\");\n            i.classList.add(\"fa-home\");\n            i.classList.add(\"fa-2x\");\n            i.classList.add(\"icon-padding\");\n            i.addEventListener('click', sendPayload);\n            toolbar.append(i);\n            \n            function displayTitle(lh) {\n                p.text(lh); \n            }\n            \n            function upTime() {\n                var d = new Date();\n                p.text(d.toLocaleTimeString([], {hour: 'numeric', minute:'2-digit'}));\n            }\n        \n            if(document.clockInterval){ \n                clearInterval(document.clockInterval);\n                document.clockInterval = null;\n            }\n                \n            document.clockInterval = setInterval(upTime, 1000);\n        }\n    });\n</script>\n","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"global","className":"","x":130,"y":200,"wires":[["fd314f635fc03051"]]},{"id":"fd314f635fc03051","type":"ui_ui_control","z":"a084448f7f5e471e","name":"","events":"all","x":360,"y":200,"wires":[[]]},{"id":"c982fbb8.1deb38","type":"ui_group","name":"hidden_group","tab":"7c447e96.4b96a","order":1,"disp":false,"width":"6","collapse":false},{"id":"7c447e96.4b96a","type":"ui_tab","name":"Zooland Sys","icon":"home","order":36,"disabled":false,"hidden":true}]

and Here is the full code for the template node I made:

<script type="text/javascript">
    $(function(scope)
    {
        if($('.md-toolbar-tools').length != 0)
            loadClock();
        else 
            setTimeout(loadClock, 500);
        
        function sendPayload(){
            scope.send({payload:"Home"});
        }
    
        function loadClock(){
            $('#clock').remove();
            var toolbar = $('.md-toolbar-tools');
            
            var div = $('<div/ class="clock-style">');
            var p = $('<p/ id="clock">');
            
            div.append(p);
            div[0].style.margin = '5px 5px 5px auto';
            toolbar.append(div);
            
            // Add element for clickable home icon if not already present
            var i = document.createElement('i');
            i.classList.add("fa");
            i.classList.add("fa-home");
            i.classList.add("fa-2x");
            i.classList.add("icon-padding");
            i.addEventListener('click', sendPayload);
            toolbar.append(i);
            
            function displayTitle(lh) {
                p.text(lh); 
            }
            
            function upTime() {
                var d = new Date();
                p.text(d.toLocaleTimeString([], {hour: 'numeric', minute:'2-digit'}));
            }
        
            if(document.clockInterval){ 
                clearInterval(document.clockInterval);
                document.clockInterval = null;
            }
                
            document.clockInterval = setInterval(upTime, 1000);
        }
    });
</script>

When running this on a blank page, the clock and Home icon appear as they should, but wen clicking on the home icon, the error I get in the Java console is:

VM36:10 Uncaught TypeError: scope.send is not a function
    at HTMLElement.sendPayload (<anonymous>:10:19)

I'm sure there is something simple I'm still missing but I think I've been staring at it too long to see it...

hello .. you didnt use in your code a self calling function and pass scope in
(see the example i sent you in the last post)

 (function (scope) {

  ....

    })(scope);

Hi @UnborN. Yes, I did miss that small detail (adding scope at the end of the self-calling function. I did two tests, in the first, I took the code as-is from your example, placed it in the global template node and ran it. Once the dashboard launched, I got this error:

Uncaught ReferenceError: scope is not defined
    at <anonymous>:52:8
    at b (app.min.js:20:866)
    at He (app.min.js:20:48373)
    at S.fn.init.append (app.min.js:20:49724)
    at D (app.min.js:589:4330)
    at app.min.js:589:7887
    at Array.forEach (<anonymous>)
    at o (app.min.js:589:7833)
    at app.min.js:589:9033
    at Socket.<anonymous> (app.min.js:590:462)

Could the fact that the ui-template node is being added to the site head section rather than a dashboard group make a difference? It really seems to be having issues getting the scope reference.

Also, is there a simpler way to accomplish this? All I want to do is make the home icon go back to the home page when clicked. Is there a way to just link to the home page through a URL?

yes .. dont add it to the head :wink:
i missed that part (you did mention it)

image

Haha ok. I assigned the template node to one of the UI-groups and yes it works now. But in this way, I would need to make a copy of this node to go on every page of the dashboard for it to work. I can do that if necessary, but are there any better approaches to do the same thing?

I was looking at the code for the Nav menu to see how it is being linked to the home page when clicked. It looks like the click event of the menu items point to a function like this: ng-click="main.open(obj, $index)". I'm not sure if this could somehow be adapted to work with my template node but this is something I am considering as an alternative.

The last idea is I noticed that the URL for the dashboard pages seem to follow an index number format: /ui/#!/0, /ui/#!/1 etc. If my homepage happens to be index 0, is it safe to assume that it will always be 0? Is the index determined by the order that the dashboard tabs are sorted in the Tabs and Links list?

It seems like it .. i did a quick test also and the Tabs dont load until you select them from the sidebar.
If you add the code to the <head> then no access to scope.

Interesting finding you did about the inner workings of the Dashboard but without a way to manipulate that code i think we are stack to adding a hidden template to each Tab .. and also add code to check if your html elements are already injected to the top navbar.

Similar Thread here:

Or an alternative is to look into node-red-contrib-uibuilder
that gives you the flexibility to create your own dashboard from scratch with full access to your own code
but that would require some knowledge of html, css, javascript or any front-end language

Yes as long as you don't reorder them. You can use ui-control with group/tab names. Here is site with some examples of ui-control

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