LED Bar-graph Display template conversion to Dashboard2.0

Hi,
I'm new to node-red , working on my first dashboard project for Amateur radio.
I am trying to create a linear LED gauge, I have installed guage-linear but I'm having issues with it working! only seems to work if the bar is set to solid and default mode, also its reaction time is rather slow so values changing at around 0.2 of a second hardley register to the correct level on the display. so I found the LED Bar-graph Display template example and this is exactly what I'm after and reacts to my data source speed, but this is in Dashboard. As soon as I copy the script to my Dashboard2.0 flow template the script doesnt work.
Running on a R-Pi4

Can anyone shed any light on the problem.

here is the script from the the webpage.

    (function($scope) {
    
let amount = 10 //amount of LEDs
let label = "LED bar Graph"
 
let on1 = "#00FF00"
let off1 = "#006600"
let on2 = "#FFFF00"
let off2 = "#666600"
let on3 = "#FF0000"
let off3 = "#660000"

let threshold1 = 1/2 // the limit between color 1 and color 2
let threshold2 = 4/5 //  the limit between color 2 and color 3

var bargraph = new Array(amount).fill("#000000")

$scope.$watch('msg', function() {
    
if ($scope.msg){
    if ($scope.msg.hasOwnProperty('payload') && typeof $scope.msg.payload == "number"){
        $scope.msg.label = label
        $scope.msg.payload = parseInt($scope.msg.payload)
    
        if ($scope.msg.payload > amount){
            $scope.msg.payload = amount
        }
        for (var i = 0; i < $scope.msg.payload; i++){
            if (i < amount*threshold1){
                bargraph[i] = on1
            }else if (i < amount*threshold2){
                bargraph[i] = on2
            }else{
                bargraph[i] = on3
            }
        }
        for (var i = $scope.msg.payload; i < amount; i++){
            if (i < amount*threshold1){
                bargraph[i] = off1
            }else if (i < amount*threshold2){
                bargraph[i] = off2
            }else{
                bargraph[i] = off3
            }
        }
        $scope.msg.bargraph = bargraph.reverse()
    }else if (typeof $scope.msg.payload !== "number"){
        $scope.msg = {"bargraph":[...bargraph], "payload": 0, "label":"Led Bar Graph"}
    }   
}else{
    $scope.msg = {"bargraph":[...bargraph], "payload": 0, "label":"Led Bar Graph"}
}
    });
})(scope);
</script>

<style>
    .bargraph {
        float: right;
        padding: 3px;
        width: 5px;
        height: 15px;
        margin: 4px 2px 8px 0px;
        border-radius: 0%;
    }
</style>

<div>{{msg.label}}
    <span ng-repeat="led in msg.bargraph track by $index">
    <span class="bargraph" style="background-color: {{led}}; box-shadow: black 0 -1px 1px 0px, inset black  0 -1px 4px, {{led}} 0 3px 15px;"></span>
    </span>
</div>

Hello @RussTEE

Dashboard 1 was based on Angular while Dashboard 2 is based on Vue front-end framework.
Here is my attempt to refactor the code based on Vue

<template>
   <div>
        <h3>{{msg.label}}</h3>
        <div style="margin-top: 10px;">
            <span v-for="led in bargraph" class="bargraph" :style="{ 'backgroundColor': led, 'boxShadow': 'black 0 0px 1px 0px inset, black 0 0px 2px,' + led + ' 0 0px 1px' }"></span>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            // define variables available component-wide
            // (in <template> and component functions)
            return {
                label : "LED bar Graph",
                amount : 10,

                on1 : "#00FF00",
                off1 : "#006600",
                on2 : "#FFFF00",
                off2 : "#666600",
                on3 : "#FF0000",
                off3 : "#660000",

                threshold1 : 1/2, // the limit between color 1 and color 2
                threshold2 : 4/5, //  the limit between color 2 and color 3
                
                bargraph : []

            }
        },
        watch: {
            // watch for any changes of msg
            msg: function () {
                if (this.msg.label) this.label = msg.label
                if (this.msg.payload && typeof this.msg.payload === "number") {       
                    this.msg.payload = parseInt(this.msg.payload)

                    if (this.msg.payload > this.amount) {
                        this.msg.payload = this.amount
                    }

                    for (var i = 0; i < this.msg.payload; i++) {
                        if (i < this.amount * this.threshold1){
                            this.bargraph[i] = this.on1
                        }
                        else if (i < this.amount * this.threshold2){
                            this.bargraph[i] = this.on2
                        }
                        else{
                            this.bargraph[i] = this.on3
                        }
                    }

                    for (var i = this.msg.payload; i < this.amount; i++) {
                        if (i < this.amount * this.threshold1){
                            this.bargraph[i] = this.off1
                        }
                        else if (i < this.amount * this.threshold2) {
                            this.bargraph[i] = this.off2
                        }
                        else{
                            this.bargraph[i] = this.off3
                        }
                    }

                    // this.msg.bargraph = this.bargraph.reverse()
                }
            }
        },
        computed: {
            // automatically compute this variable
            // whenever VueJS deems appropriate
        },
        methods: {
            // expose a method to our <template> and Vue Application
          
        },
        mounted() {
            // code here when the component is first loaded
            this.bargraph = new Array(this.amount).fill("#000000");
    
        },
        unmounted() {
            // code here when the component is removed from the Dashboard
            // i.e. when the user navigates away from the page
        }
    }
</script>

<style scoped>
    .bargraph {
        /* float: right; */
        padding: 3px;
        width: 5px;
        height: 15px;
        margin: 4px 2px 8px 0px;
        border-radius: 0%;
    }
</style>

Im sure the code could be optimized a bit more but i hope you find it useful enough for your project.

Test Flow :

[{"id":"243c25f6c65163eb","type":"ui-template","z":"54efb553244c241f","group":"c72c76e21d82b7d1","page":"","ui":"","name":"","order":1,"width":0,"height":0,"head":"","format":"<template>\n   <div>\n        <h3>{{msg.label}}</h3>\n        <div style=\"margin-top: 10px;\">\n            <span v-for=\"led in bargraph\" class=\"bargraph\" :style=\"{ 'backgroundColor': led, 'boxShadow': 'black 0 0px 1px 0px inset, black 0 0px 2px,' + led + ' 0 0px 1px' }\"></span>\n        </div>\n    </div>\n</template>\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                label : \"LED bar Graph\",\n                amount : 10,\n\n                on1 : \"#00FF00\",\n                off1 : \"#006600\",\n                on2 : \"#FFFF00\",\n                off2 : \"#666600\",\n                on3 : \"#FF0000\",\n                off3 : \"#660000\",\n\n                threshold1 : 1/2, // the limit between color 1 and color 2\n                threshold2 : 4/5, //  the limit between color 2 and color 3\n                \n                bargraph : []\n\n            }\n        },\n        watch: {\n            // watch for any changes of msg\n            msg: function () {\n                if (this.msg.label) this.label = msg.label\n                if (this.msg.payload && typeof this.msg.payload === \"number\") {       \n                    this.msg.payload = parseInt(this.msg.payload)\n\n                    if (this.msg.payload > this.amount) {\n                        this.msg.payload = this.amount\n                    }\n\n                    for (var i = 0; i < this.msg.payload; i++) {\n                        if (i < this.amount * this.threshold1){\n                            this.bargraph[i] = this.on1\n                        }\n                        else if (i < this.amount * this.threshold2){\n                            this.bargraph[i] = this.on2\n                        }\n                        else{\n                            this.bargraph[i] = this.on3\n                        }\n                    }\n\n                    for (var i = this.msg.payload; i < this.amount; i++) {\n                        if (i < this.amount * this.threshold1){\n                            this.bargraph[i] = this.off1\n                        }\n                        else if (i < this.amount * this.threshold2) {\n                            this.bargraph[i] = this.off2\n                        }\n                        else{\n                            this.bargraph[i] = this.off3\n                        }\n                    }\n\n                    // this.msg.bargraph = this.bargraph.reverse()\n                }\n            }\n        },\n        computed: {\n            // automatically compute this variable\n            // whenever VueJS deems appropriate\n        },\n        methods: {\n            // expose a method to our <template> and Vue Application\n          \n        },\n        mounted() {\n            // code here when the component is first loaded\n            this.bargraph = new Array(this.amount).fill(\"#000000\");\n    \n        },\n        unmounted() {\n            // code here when the component is removed from the Dashboard\n            // i.e. when the user navigates away from the page\n        }\n    }\n</script>\n\n<style scoped>\n    .bargraph {\n        /* float: right; */\n        padding: 3px;\n        width: 5px;\n        height: 15px;\n        margin: 4px 2px 8px 0px;\n        border-radius: 0%;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":400,"y":2780,"wires":[[]]},{"id":"adba9a79062451ae","type":"inject","z":"54efb553244c241f","name":"8.3","props":[{"p":"payload"},{"p":"label","v":"Test Title 1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"8.3","payloadType":"num","x":190,"y":2740,"wires":[["243c25f6c65163eb"]]},{"id":"92806918d4f09c74","type":"inject","z":"54efb553244c241f","name":"3.1","props":[{"p":"payload"},{"p":"label","v":"Test Title 2","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3.1","payloadType":"num","x":190,"y":2840,"wires":[["243c25f6c65163eb"]]},{"id":"c72c76e21d82b7d1","type":"ui-group","name":"My Group","page":"9d4bc1caffe751ab","width":"12","height":"10","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"9d4bc1caffe751ab","type":"ui-page","name":"My Page","ui":"a171c8195c1b8e57","path":"/ui-button-example","icon":"button-pointer","layout":"grid","theme":"9d8bfd7e0d216779","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":1,"className":"","visible":"true","disabled":"false"},{"id":"a171c8195c1b8e57","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":false,"acceptsClientConfig":["ui-control","ui-gauge","ui-chart","ui-form","ui-text-input","ui-file-input","ui-button","ui-button-group","ui-dropdown","ui-radio-group","ui-slider","ui-switch","ui-text","ui-table","ui-markdown","ui-notification","ui-template"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"icon","titleBarStyle":"default","showReconnectNotification":false,"notificationDisplayTime":5,"showDisconnectNotification":false},{"id":"9d8bfd7e0d216779","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#15617e","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px","density":"default"}}]
2 Likes

HI @UnborN

many thanks for your help, works a treat.
I have so much learning to do!

cheers