Node-RED Dashboard Template Examples (AngularJS)

Published: | by Julian Knight Reading time ~3 min.
📖 Kb | 📎 Development | 🔖 Node-RED

Some examples of using AngularJS with the Node-RED Dashboard. Not all features of Angular are accessible via the Dashboard.

Accessing the msg object 🔗︎

When using Dashboard, the Node-RED server (the back end) sends data via Socket.IO to the client browser (the front end). This comes in the form of a msg object. in similar form to the msg that flows through the rest of Node-RED.

To access the msg object from within a client Angular script. Put this into a Dashboard Template node:

<div ng-bind-html="msg.payload"></div>
<script>
    //console.dir(scope) // this also works
    //console.dir(scope.msg) // This doesn't because scope.msg doesn't yet exist

    // Lambda function to access the Angular Scope
    ;(function(scope) {
        //console.log('--- SCOPE ---')
        //console.dir(scope) // this works but you only get it once (on startup)
        //console.dir(scope.msg) // Doesn't work for  because scope.msg doesn't yet exist

        //Have to use $watch so we pick up new, incoming msg's
        scope.$watch('msg.payload', function(newVal, oldVal) {
            console.log('- Scope.msg -')
            console.dir(scope.msg)
        })

    })(scope)
</script>

A couple of ways to send a msg back to Node-RED 🔗︎

This shows you how to set up some buttons and a slider widget that send data back to Node-RED. As always, add this code to a Dashboard Template node.

<div>{{msg.payload}}</div>

<md-button ng-click="send({payload: 'Hello World'})">
    Click me to send a hello world
</md-button>

<md-slider ng-model="msg.payload" ng-change="send(msg)"></md-slider>

<div flex layout="row" layout-align="space-around center">
    <md-button ng-repeat="b in buttons" class="md-icon-button" ng-click="click(b)">
        <ng-md-icon icon="{{msg.payload[b.payload]?b.icon2:b.icon}}"
                    ng-style="{color: msg.payload[b.payload]?b.color2:b.color}"></ng-md-icon>
    </md-button>
</div>

<script>
    scope.buttons = [{
        icon: 'pause', color: 'black',
        icon2: 'play_arrow', color2: 'red',
        payload: 'play',
    }, {
        icon: 'alarm', color: 'black',
        icon2: 'alarm', color2: 'red',
        payload: 'alarm',
    }];

    scope.click = function(b) {
        if (!this.msg) this.msg = {};
        if (!this.msg.payload) this.msg.payload = {};
        this.msg.payload[b.payload] = !this.msg.payload[b.payload];
        this.send(this.msg);
    }.bind(scope);
</script>

Ways to update the UI 🔗︎

Using Anglular and a Dashboard Template, we can update the UI direct from an incoming msg, by doing a calculation after receiving a message or by calling a function.

<h1>Msg Topic: {{msg.topic}}</h1>
<h2>msg.payload.a</h2>
<div ng-bind-html="msg.payload.a"></div>
<h2>Calculated after receiving a msg</h2>
<div>{{myCalc}}</div>
<h2>Call a function on click</h2>
<div ng-click="click(b)">Click me to change the number</div>
<div>{{myNum}}</div>

<script>
    // Lambda function to access the Angular Scope
    ;(function(scope) {
        //Have to use $watch so we pick up new, incoming msg's
        scope.$watch('msg.payload', function(newVal, oldVal) {
            console.log('- Scope.msg -')
            console.dir(scope.msg)

            scope.myCalc = scope.msg.payload.b * 2
        })

        // Function triggered by clicking on a div
        scope.click = function(b) {
            if ( ! scope.myNum ) scope.myNum = 0
            scope.myNum++
        }
    })(scope)
</script>

Date Picker Example 🔗︎

<div>
   <md-datepicker ng-model="myDate" md-placeholder="Enter date" ng-change="send({payload: myDate})"></md-datepicker>
</div>

Dynamically inject HTML 🔗︎

Use this code in a Dashboard Template to render a msg.payload containing HMTL.

<div ng-bind-html="msg.payload"></div>

Use an input array or object to create a list 🔗︎

Angular has an ng-repeat property that lets us walk through an array (or object) to dynamically create a list, table, etc.

<div ng-repeat="(key, value) in myObj"> ... </div>

Use a tracking ID if possible as this helps Angular know when something needs updating:

<div ng-repeat="(key, value) in myObj track by myObj.id"> ... </div>

You can also use ng-repeat-start and ng-repeat-end elements to wrap content.

See the Angular v1 docs for ngRepeat for more details.

Working example, use this in a Dashboard Template node:

<table>
    <tr ng-repeat="(key, value) in msg.payload">
        <td>{{key}}</td>
        <td>{{value}}</td>
    </tr>
</table>

Change the Angular delimiters 🔗︎

If you want to send data through a Node-RED Template node before sending it to a Dashboard Template node, you will find that, since both use the same delimiters (double curly brackets). So you need to change the delimiters in the Dashboard Template.

<!DOCTYPE html>
<html ng-app="ngApp">
<head></head>
<body ng-controller="ngCtrl">

    <h1>Topic: {[{topic}]}</h1>
    <pre>
{[{payload | json }]}
    </pre>


    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.12/angular.min.js"></script>
    <script>
        var ngApp = angular.module('ngApp', []);

        ngApp.config(function($interpolateProvider) {
            $interpolateProvider.startSymbol('{[{');
            $interpolateProvider.endSymbol('}]}');
        });

        ngApp.controller('ngCtrl', ['$scope', function($scope) {
            // Need to wrap with TRY in case not valid JSON
            $scope.payload = JSON.parse(decodeEntities('{{payload}}'));
            $scope.topic = '{{topic}}';
        }]);

        function decodeEntities(encodedString) {
            var textArea = document.createElement('textarea');
            textArea.innerHTML = encodedString;
            return textArea.value;
        }
    </script>
</body>
</html>

comments powered by Disqus