Access msg object in dashboard ui_template


#1

Hi,
I have implemented a Google Maps interface using ui_template node with some functionality. Next step for me is to load the markers from a database, as they are currently hardcoded. I figured that this would be possible if I trigger a query everytime the "Maps" tab is refreshed.
However the template code will not "run" after a

var len = msg.payload.length 

command, neither can I access the

msg.payload[0].lat
msg.payload[0].lng

as they are returned from the query.

I am posting the necessary nodes from the flow + a screenshot from the db returned items below.
Please note that a Gmaps API key is necessary for the script to work (bottom of the code).
If you don't have one, it would be equally good if you could just tell me how to access the msg object from
within the ui_template node.
Thank you in advance

EDIT: I've already tried to include everything in

(function(scope)
    scope.$watch('msg',function(msg){
    .
    .
    .
})(scope);
[{"id":"5be741bd.6e203","type":"ui_template","z":"ba566a8f.bd7bc8","group":"10ede6b0.919749","name":"Map","order":0,"width":"27","height":"16","format":"<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <title>My Map</title>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <meta charset=\"utf-8\">\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css\">\n    \n    <style>\n        /* Always set the map height explicitly to define the size of the div\n         * element that contains the map. */\n        #map {\n          height: 100%;\n          /*margin-left: 500px;*/\n          padding: 0;\n    \n        }\n        /* Optional: Makes the sample page fill the window. */\n        body, html{\n          height: 100%;\n          padding: 0;\n        }\n        ol{\n            display:none;\n            font-weight:bold;\n            list-style-type:none;\n           /* background-color:black; */\n        }\n        li{\n            /*background-color:white; */\n            margin: 1px 0 0 0;\n            list-style-type:none;\n            cursor:pointer;\n            color:#000;\n        }\n        \n    </style>\n    \n  </head>\n\n  <body>\n\n  <div id=\"map\"></div>\n  \n  <ol id=\"ol0\">\n      <li onclick=\"theScope.send({topic:'unit1'});\">Sensor1</li>\n      <li onclick=\"theScope.send({topic:'unit2'});\">Sensor2</li>\n      <li onclick=\"theScope.send({topic:'unit3'});\">Sensor3</li>\n      <li onclick=\"theScope.send({topic:'unit4'});\">Sensor4</li>\n      <li onclick=\"theScope.send({topic:'unit5'});\">Sensor5</li>\n  </ol>\n  <ol id=\"ol1\">\n      <li onclick=\"theScope.send({topic:'unit1'});\">Sensor1</li>\n      <li onclick=\"theScope.send({topic:'unit2'});\">Sensor2</li>\n      <li onclick=\"theScope.send({topic:'unit3'});\">Sensor3</li>\n      <li onclick=\"theScope.send({topic:'unit4'});\">Sensor4</li>\n  </ol>\n  <ol id=\"ol2\">\n      <li onclick=\"theScope.send({topic:'unit1'});\">Sensor1</li>\n      <li onclick=\"theScope.send({topic:'unit2'});\">Sensor2</li>\n      <li onclick=\"theScope.send({topic:'unit3'});\">Sensor3</li>\n  </ol>\n  <ol id=\"ol3\">\n      <li onclick=\"theScope.send({topic:'unit1'});\">Sensor1</li>\n      <li onclick=\"theScope.send({topic:'unit2'});\">Sensor2</li>\n  </ol>\n  <button class=\"inner\" id=\"myBtn1\" ng-click=\"send({topic: '1'})\">Archive</button>\n\n    <script>\n        var stockholm = {lat: 59.334591, lng: 18.063240};\n        var newYork = {lat:40.730610 ,lng: -73.935242};\n        var beijing = {lat:39.913818,lng:116.363625};\n        var thessaloniki = {lat: 40.736851,lng: 22.920227};\n        \n        var value=\"Dashbrd\";\n        var map;\n        var theScope = scope;\n        \n        var imHereAgain = \"im here again\";\n        var overIt = \"Over it\";\n        \n        theScope.send({payload:overIt});\n        \n      function initMap() \n      {\n        var options={\n          zoom:3,\n          center:stockholm\n        }\n        \n      \n      map = new google.maps.Map(document.getElementById('map'), options);\n      \n    var infoWindow = new google.maps.InfoWindow();\n    \n        function _newGoogleMapsMarker(param) {\n            var r = new google.maps.Marker({\n            map: param._map,\n            position: new google.maps.LatLng(param._lat, param._lng),\n            title: param._head,\n            animation: google.maps.Animation.DROP\n        });\n            if (param._data) {\n                google.maps.event.addListener(r, 'click', function() {\n                    // this -> the marker on which the onclick event is being attached\n                    if (!this.getMap()._infoWindow) {\n                        this.getMap()._infoWindow = new google.maps.InfoWindow();\n                    }\n                    this.getMap()._infoWindow.close();\n                    param._data.style.display=\"block\";\n                    this.getMap()._infoWindow.setContent(param._data);\n                    this.getMap()._infoWindow.open(this.getMap(), this);\n                });\n            }\n            return r;\n    }\n    \n    var dealer_markers = [{\n\n        //Random UK spot\n        lat: '51.595212',\n        lng: '-2.400068',\n        position: '1'\n    },\n    {\n        //New York\n        lat: '40.730610',\n        lng: '-73.935242',\n        position: '2'\n    },\n    {\n        //Thessaloniki\n        lat: '40.736851',\n        lng: '22.920227',\n        position: '3'\n    },\n    {\n        //Stockholm\n        lat: '59.334591',\n        lng: '18.063240',\n        position: '4'\n    }];\n        /*for (var a = 0; a < dealer_markers.length; a++) {\n            theScope.send({payload:imHereAgain});\n        var tmpLat = dealer_markers[a].lat;\n        var tmpLng = dealer_markers[a].lng;\n        var cont=document.getElementById('ol'+a); */\n       \n        var len = msg.payload.length;\n        theScope.send({payload:overIt});\n        for (var a = 0; a < len; a++) {\n            //theScope.send({payload:imHereAgain});\n            var tmpLat = msg.payload[a].lat;\n            var tmpLng = msg.payload[a].lng;\n        //var cont=document.getElementById('ol'+a); \n\n        var marker = _newGoogleMapsMarker({\n            _map: map,\n            _lat: tmpLat,\n            _lng: tmpLng,\n            _head: '|' + new google.maps.LatLng(tmpLat, tmpLng),\n            _data: cont\n        });\n    } \n}\n    </script>\n    \n    <script id=\"titleScript\" type=\"text/javascript\">\n    $('#myID').remove();\n    var toolbar = $('.md-toolbar-tools');\n    var div = $('<div/ id=\"myID\">');\n    //var div = document.getElementById(\"divara\");\n    //var form = $('<form/ id=\"forma\">');\n    var input = $('<input/ id=\"inpt\" type=\"text\" name=\"searchTerm\" placeholder=\"Search..\" style=\"font-style:italic; background-color:#016064; border:none\">');\n    var btn = $('<button/  class=\"fa fa-search\" onclick=\"theScope.send({payload:1,topic:takeInput()});\" style=\"height:32px; width:32px; border:none; background:transparent; padding: 8px 15px 8px 5px ; \">');\n        \n    function takeInput(){\n        var x = document.getElementById(\"inpt\").value;\n        return x;\n    }\n \n  //  $('#titleScript').parent().hide();\n    //form.append(input);\n    //form.append(btn);\n    div.append(input);\n    div.append(btn);\n    div[0].style.margin = '5px 5px 5px auto';\n    toolbar.append(div);\n\n    </script>\n    \n    <script src=\"https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE&callback=initMap\"\n    async defer></script>\n  </body>\n</html>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":571.1000366210938,"y":582.6000366210938,"wires":[["34d604c0.d80f5c","8e105e9d.1d75d","83a41ec1.a12f8"]]},{"id":"34d604c0.d80f5c","type":"debug","z":"ba566a8f.bd7bc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":474,"y":490,"wires":[]},{"id":"3b392773.a32f08","type":"mysql","z":"ba566a8f.bd7bc8","mydb":"cca3b54f.41e9a8","name":"","x":417,"y":582,"wires":[["b911ee3e.14aea","5be741bd.6e203"]]},{"id":"6558677c.4475a8","type":"function","z":"ba566a8f.bd7bc8","name":"pin query","func":"msg.topic=\"SELECT * FROM pins\";\nreturn msg;","outputs":1,"noerr":0,"x":280,"y":605,"wires":[["3b392773.a32f08"]]},{"id":"138f13a8.59c3fc","type":"link in","z":"ba566a8f.bd7bc8","name":"","links":["80cbf799.5988a8","83a41ec1.a12f8"],"x":162,"y":569,"wires":[["6558677c.4475a8"]]},{"id":"b911ee3e.14aea","type":"debug","z":"ba566a8f.bd7bc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":267,"y":469,"wires":[]},{"id":"83a41ec1.a12f8","type":"link out","z":"ba566a8f.bd7bc8","name":"","links":["138f13a8.59c3fc"],"x":589,"y":648,"wires":[]},{"id":"10ede6b0.919749","type":"ui_group","z":"","name":"Map","tab":"511291be.008a","disp":false,"width":"27","collapse":false},{"id":"cca3b54f.41e9a8","type":"MySQLdatabase","z":"","host":"127.0.0.1","port":"3306","db":"maps","tz":""},{"id":"511291be.008a","type":"ui_tab","z":"","name":"Map","icon":"map"}]

image


#2

Did you try:

var len = {{msg.payload.length}}
?

same applies to the properties:

{{msg.payload[0].lat}}
{{msg.payload[0].lng}}

#3

Thanks for your answer.
Yes I have.
However this does not work in <script> tags


#4

And this?

$scope.msg.id
$scope.msg.lat
$scope.msg.lng
$scope.msg.position


#5

Thanks krambriw.
To use that I have to wrap the necessary part in function(scope) etc (?)
Whenever I wrap something in

(function(scope){
     scope.$watch('msg',function(){
          ...
   })
})(scope)

the map in the dashboard template does not even render.
Most likely I am typing something the wrong way because that seems to be the answer everywhere I looked.
Can someone please provide me with a working simple example of pushing a msg, with the structure given in the screenshot, into a dashboard template node and output from the ui_templ just the msg.payload[0].lat or something?
It would be just what I need.
Jason

EDIT: output it with code in <script> tags


#6

Well, this example works if you put it into a ui_template node and sends a json payload to the same

Example (I did put it into an inject node)

{"id":1,"lat":51.59,"lng":-2.4,"position":"UK"}
<!DOCTYPE html>
<html>
<script type="text/javascript">
theScope = scope;

// Watch the payload and update
(function(scope) {
    scope.$watch('msg.payload', function(data) {
        update(data);
    });
})(scope);

function update(dta) {
    theScope.send({payload:dta.id});
    theScope.send({payload:dta.lat});
    theScope.send({payload:dta.lng});
    theScope.send({payload:dta.position});
}
</script>
</html>

But to make it work; I keep the dashboard page open where the ui_template node is placed. I suppose that is the intention, you would like to update a dashboard with the values?


#7

just checking you are aware of the node-red-contrib-web-worldmap node that can do maps in the dashboard.


#8

Hello dceejay,
yes I am however its description states that markers and other elements on it are not clickable
yet and attaching actionlisteners to markers is essential for my application.


#9

Thank you krambriw, your answer pointed me to the right direction for what I wanted!
The trick was executing the

initMap(data)

google maps callback function in place of your " update(data) ".
Then I could normally access the msg returned from the query like

for (var a = 0; a < data.length; a++) {
            var tmpLat = data[a].lat;
            var tmpLng = data[a].lng;   

        var marker = _newGoogleMapsMarker({
            _map: map,
            _lat: tmpLat,
            _lng: tmpLng
        }); 
        
 }

#10

Ah that was soooo yesterday.. I think they are now :slight_smile: