Dashboard 2 Beta development

By a process of elimination, I've narrowed that error message down to a template node which was an example provided by joepavitt posted in the forum a few days ago.

colors

By removing that flow, the error disappears.

[{"id":"9860377bb9db048d","type":"ui-template","z":"03cd1d2be26baaa9","group":"cb03a2a3aec89425","dashboard":"ID-BASE-1","page":"ID-PAGE-1","name":"","order":0,"width":0,"height":0,"head":"","format":"<template>\n<v-icon\n    :color=\"msg.payload.state === 'on' ? (msg.payload?.color || 'green') : msg.payload.color\"\n    :icon=\"msg.payload.state === 'on' ? 'mdi-led-on' : 'mdi-led-off'\"\n    >\n</v-icon\n</template>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":440,"y":780,"wires":[[]]},{"id":"54055f52c82e816c","type":"function","z":"03cd1d2be26baaa9","name":"generateRandomColorName","func":"const colorNames = [\"red\", \"pink\", \"yellow\", \"green\", \"purple\", \"blue\", \"cyan\", \"lime\", \"orange\"];\n\nfunction generateRandomColorName() {\n  const randomIndex = Math.floor(Math.random() * colorNames.length);\n  return colorNames[randomIndex];\n}\n\nmsg.payload = {\n  state: 'on',\n  color: generateRandomColorName()\n}\nreturn msg;","outputs":1,"timeout":30,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":820,"wires":[["9860377bb9db048d"]]},{"id":"9b32f3d3ad2c803b","type":"inject","z":"03cd1d2be26baaa9","name":"on","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":150,"y":820,"wires":[["54055f52c82e816c"]]},{"id":"140afad3a3b38f46","type":"inject","z":"03cd1d2be26baaa9","name":"off","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"state\": \"off\" }","payloadType":"json","x":150,"y":780,"wires":[["9860377bb9db048d"]]},{"id":"cb03a2a3aec89425","type":"ui-group","name":"Test","page":"19eb6d108e9275e2","width":"25","height":"15","order":-1,"showTitle":true,"className":""},{"id":"ID-BASE-1","type":"ui-base","name":"Dashboard","path":"/dashboard"},{"id":"ID-PAGE-1","type":"ui-page","name":"Page 1","ui":"ID-BASE-1","path":"/page1","layout":"notebook","theme":"f9b6670b127dc219"},{"id":"19eb6d108e9275e2","type":"ui-page","name":"Examples","ui":"ID-BASE-1","path":"/examples","layout":"grid","theme":"a965ccfef139317a","order":-1,"className":""},{"id":"f9b6670b127dc219","type":"ui-theme","name":"FlowForge Theme","colors":{"surface":"#152a47","primary":"#005aff","bgPage":"#ffffff","groupBg":"#ffffff","groupOutline":"#cc3e3e"}},{"id":"a965ccfef139317a","type":"ui-theme","name":"Default","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}]
1 Like

Yes please Colin.

Could you also apply conditional chaining to any msg props to see if that reduces the stream of errors? (e.g. bind to msg?.payload?.xyz instead of msg.payload.xyz)

Also, if you can determine if this is solely UI-Template and/or other nodes.

Joe and I appreciate the feedback very much - thank you.

Don't believe it was me that post it - but @Steve-Mcl has nailed it:

msg.payload doesn't exist at the very start, until you inject something in. So, where we're trying to read msg.payload.state in the :color and :icon properties, msg.payload is undefined on first load.

If instead, we do:

<template>
<v-icon
    :color="msg?.payload?.state === 'on' ? (msg?.payload?.color || 'green') : (msg?.payload?.color || 'grey')"
    :icon="msg?.payload?.state === 'on' ? 'mdi-led-on' : 'mdi-led-off'"
    >
</v-icon
</template>

That should work

1 Like

Apologies Joe, yes now I think back, it wasn't you :face_with_raised_eyebrow:

...and yes, your revised code as above does stop the error occurring for me.

@Colin do you have the same code snippet saved like I had?

1 Like

Yes, that and other similar templates was the cause of most of the errors.

I do however have one left, which may be caused by a strange state that I have got my flows into.

In the browser console I see

Note that there are two ui base configs, and I do see both in Configuration Nodes, and they are both in use. However there is only one group config so it should not be possible to have two ui-base configs, both in use. I looked through flows.json and found that most of the ui-templates are of the form

{
        "id": "538c4bc8c9b098bf",
        "type": "ui-template",
        "z": "997da33a0beedade",
        "d": true,
        "group": "7545fdb22e4a388c",
        "dashboard": "ID-BASE-1",
 ...

but one is

 {
        "id": "1c35624c74ddbdcf",
        "type": "ui-template",
        "z": "997da33a0beedade",
        "d": true,
        "group": "7545fdb22e4a388c",
        "dashboard": "04ee189a49c54f22",
...

Note that it has the same group id but different dashboard id. Why does a template have both group and dashboard?

Issue raised:

D2 fails if httpNodeRoot is non-blank · Issue #437 · FlowFuse/node-red-dashboard (github.com)

1 Like

Now a composite element whose purpose is to turn a dimmable lamp on and off. I'm still having problems knowing how to extract the value set in the "v-slider" component in order to expel it in the output in the node and make the rest of the button relate to it.. the objective is always to make the element react to the input and imitate at the output its value. Below is a visual example of the component and also the code.

<template>
    <v-card ref="card">
        <v-btn ref="botao" stacked @click="alternar">
            Bebedouro
            <v-icon ref="icon">mdi-water-pump</v-icon>            
        </v-btn>
        <v-slider ref="barra"></v-slider>
    </v-card>    
</template>

<script>
    export default {
        data() {
            return {
                chave: 'a13'                
            };
        },
        methods: {
            alternar: function () {
                if (window.localStorage.getItem(this.chave) === 'ON') {
                    this.desligar();
                    this.send({ payload: 'OFF' });
                } else if (window.localStorage.getItem(this.chave) === 'OFF') {
                    this.ligar();
                    this.send({ payload: 'ON' });
                } else {
                    this.desligar();
                }
            },
            ligar: function () {            
                window.localStorage.setItem(this.chave, 'ON');
                this.$refs.icon.$el.style.color = '#BD9608';
                this.$refs.icon.$el.style.textShadow = '0px 0px 10px #BD9608';                              
            },
            desligar: function () {                
                window.localStorage.setItem(this.chave, 'OFF');
                this.$refs.icon.$el.style.color = '#A9A9A9';
                this.$refs.icon.$el.style.textShadow = '0px 0px 0px';                              
            },
            barra: function () {
            
            },
            setState (value) {
                if (value === 'ON') {                    
                    this.ligar();
                } else if (value === 'OFF') {                    
                    this.desligar();
                }
            }
        },
        mounted () {          
            this.setState(window.localStorage.getItem(this.chave));         
            this.$socket.on("msg-input:" + this.id, (msg) => {
                this.setState(msg.payload);
            });
            this.$nextTick(() => {
                // tanho do icone
                this.$refs.icon.$el.style.fontSize = '40px'; 
                // -----------------------------------------------------
                // altura do botão
                this.$refs.botao.$el.style.width = '75px';
                // largura do botão
                this.$refs.botao.$el.style.height = '45px';
                // arredondamento dos cantos do botão
                this.$refs.botao.$el.style.borderRadius = '18px';
                // cor da letra do titulo do botão
                this.$refs.botao.$el.style.color = '#000000';
                // tamanho da fonte do titulo do botão
                this.$refs.botao.$el.style.fontSize = '10px';
                // cor do fundo do botão
                this.$refs.botao.$el.style.backgroundColor = '#4F4F4F';
                //--------------------------------------------------------
                // cor do fundo do card
                this.$refs.card.$el.style.backgroundColor = '#4F4F4F';
                // arredondamento dos cantos do botão
                this.$refs.card.$el.style.borderRadius = '18px';
                this.$refs.card.$el.style.border = '1px solid #000000';
                // altura do botão
                this.$refs.card.$el.style.width = '75px';
                // largura do botão
                this.$refs.card.$el.style.height = '100px';
                //--------------------------------------------------------
                this.$refs.barra.$el.style.color = '#000000';
                
                
            });                    
        }
    }
</script>


image

1 Like

Current unsuccessful attempt.

<template>
    
    <v-slider ref="barra" @click="barra"></v-slider>
      
</template>

<script>
    export default {
        
        methods: {
            
            barra: function () {                  
                this.send({ payload: this.$refs.barra.value }); 
            },
            
        },
        
    }
</script>

Try using the @end event instead of click. Will trigger a function when you stop sliding (v-slider API)

<template>
    <h1>{{barraValue}}</h1>
    <v-slider ref="barra" v-model="barraValue" @end="barra"></v-slider>
</template>

<script>
    export default {
        data() {
            return {
                barraValue: 0
            }
        },
        
        methods: {
            barra() {      
                console.log("slider change", this.barraValue)         
                this.send({ payload: this.barraValue}); 
            }
        },
        
    }
</script>
1 Like

The output is emitting true and not the value "barraValue"

i edited the above post to fix the mistake.
Had payload: value should have been payload: this.barraValue

1 Like

Perfect my friend. Thanks a lot for the help..

<template>    
    <v-slider ref="barra" v-model="barraValue" @end="barra"></v-slider>
</template>

<script>
    export default { 

        data() {
            return {
                barraValue: 0
            }
        },
        
        methods: {
            barra() {          
                this.send({ payload: this.barraValue }); 
            }
        },
        
    }
</script>

1 Like

how to adjust the color with "trumb"... I'm not finding how to put this like in the code below.

image

<template>
    <v-card ref="card" style="background-color: #4F4F4F; border: 1px solid #000000; border-radius: 18px;" width="75" height="105">
        <v-btn ref="botao" style="background-color: #4F4F4F; font-size: 14px; border-radius: 18px;" color="#ffffff" width="75" height="75" stacked @click="alternar">
            Luz
            <v-icon ref="icon" size="45">mdi-lightbulb-variant-outline</v-icon>            
        </v-btn>
        <v-slider ref="barra" width="50" min="1" max="100" color="#A9A9A9" thumb-size="12" v-model="barravalor" @end="deslisamento"></v-slider>        
    </v-card>      
</template>

<script>
    export default {
        data() {
            return {
                chave: 'a20',
                barravalor: 0
            };
        },
        methods: {
            alternar: function () {
                if (window.localStorage.getItem(this.chave) === 'ON') {
                    this.desligar();
                    this.send({ status: 'OFF' });
                } else if (window.localStorage.getItem(this.chave) === 'OFF') {
                    this.ligar();
                    this.send({ status: 'ON' });
                } else {
                    this.desligar();
                }
            },
            ligar: function () {            
                window.localStorage.setItem(this.chave, 'ON');
                this.$refs.icon.$el.style.color = '#BD9608';
                this.$refs.icon.$el.style.textShadow = '0px 0px 10px #BD9608';
                this.barravalor = 1;
                
            },
            desligar: function () {                
                window.localStorage.setItem(this.chave, 'OFF');
                this.$refs.icon.$el.style.color = '#A9A9A9';
                this.$refs.icon.$el.style.textShadow = '0px 0px 0px'; 
                this.barravalor = 0;
            },
            deslisamento: function () {
                if (window.localStorage.getItem(this.chave) === "ON") {
                    this.send({ brilho: this.barravalor });
                } else {
                    this.barravalor = 0;
                }       
            },
            setState (value) {
                if (value === 'ON') {                    
                    this.ligar();
                } else if (value === 'OFF') {                    
                    this.desligar();
                }
            },
        },
        mounted () {          
            this.setState(window.localStorage.getItem(this.chave));         
            this.$socket.on("msg-input:" + this.id, (msg) => {
                this.setState(msg.payload);
            });                               
        }
    }
</script>


I don't know if it is wrong or right way but you can add a class for the template and then override the thumb color.

v-slider should allow to set the thumb color by

<v-slider  thumb-color="red" ></v-slider>

but it doesn't work at least for me that way.

@hotNipi

First I would like to congratulate your work, it was thanks to an example of your button that started my journey shaping panels for automation in Brazil and that is how I made this my profession. I am grateful for your commitment to helping us and I would like to clarify at the outset that I am self-taught and will probably not reach you. Returning to the subject of the Button.. it turns on and off and dims a zigbee lamp via mqtt.. in 1.0 I already mastered these manipulations which in 2.0 I'm struggling a lot. Even with your example, I still haven't managed it and I'm going to post my entire evolution and all the components I create with your help. I thank you from the bottom of my heart.

2 Likes

I'm at that point right now. All the button is ok.. working perfectly but the v-slider component doesn't behave like in the model.

Visually the button looks like this.

image

<template>
    <v-card ref="card" style="background-color: #4F4F4F; border: 1px solid #000000; border-radius: 18px;" width="75" height="105">
        <v-btn ref="botao" style="background-color: #4F4F4F; font-size: 14px; border-radius: 18px;" color="#ffffff" width="75" height="75" stacked @click="alternar">
            Luz
            <v-icon ref="icon" size="45">mdi-lightbulb-variant-outline</v-icon>            
        </v-btn>
        <v-slider ref="barra" width="50" min="1" max="100" thumb-size="12" v-model="barravalor" @end="deslisamento"></v-slider>        
    </v-card>      
</template>

<script>
    export default {
        data() {
            return {
                chave: 'a20',
                barravalor: 0
            };
        },
        methods: {
            alternar: function () {
                if (window.localStorage.getItem(this.chave) === 'ON') {
                    this.desligar();
                    this.send({ status: 'OFF' });
                } else if (window.localStorage.getItem(this.chave) === 'OFF') {
                    this.ligar();
                    this.send({ status: 'ON' });
                } else {
                    this.desligar();
                }
            },
            ligar: function () {            
                window.localStorage.setItem(this.chave, 'ON');
                this.$refs.icon.$el.style.color = '#BD9608';
                this.$refs.icon.$el.style.textShadow = '0px 0px 10px #BD9608';
                this.barravalor = 1;
                
            },
            desligar: function () {                
                window.localStorage.setItem(this.chave, 'OFF');
                this.$refs.icon.$el.style.color = '#A9A9A9';
                this.$refs.icon.$el.style.textShadow = '0px 0px 0px'; 
                this.barravalor = 0;
            },
            deslisamento: function () {
                if (window.localStorage.getItem(this.chave) === "ON") {
                    this.send({ brilho: parseInt(this.barravalor) });
                } else {
                    this.barravalor = 0;
                }       
            },
            setState (value) {
                if (value === 'ON') {                    
                    this.ligar();
                } else if (value === 'OFF') {                    
                    this.desligar();
                }
            },
        },
        mounted () {          
            this.setState(window.localStorage.getItem(this.chave));         
            this.$socket.on("msg-input:" + this.id, (msg) => {
                this.setState(msg.payload);
            });                               
        }
    }
</script>

I want to apply this feature to the model in a clean way as in the code.

So why don't you add the color="orange" attribute like you did in your above screenshot?

E.g

<template>
    <v-card ref="card" style="background-color: #4F4F4F; border: 1px solid #000000; border-radius: 18px;" width="75" height="105">
        <v-btn ref="botao" style="background-color: #4F4F4F; font-size: 14px; border-radius: 18px;" color="#ffffff" width="75" height="75" stacked @click="alternar">
            Luz
            <v-icon ref="icon" size="45">mdi-lightbulb-variant-outline</v-icon>            
        </v-btn>
        <v-slider ref="barra" width="50" min="1" max="100" thumb-size="12" v-model="barravalor" color="orange" @end="deslisamento"></v-slider>        
    </v-card>      
</template>

Am I missing something?

I appreciate the help. I tried this right away but the thumb doesn't take on the color like in the example.

That's why I posted the CSS solution. I don't know why regular stuff doesn't work. Vue is not my thing, I have very basic knowledge about it.
But CSS is regular and it works all the time if done properly. Or should I say the only thing which to use to make things look as required.

@hotNipi

I would continue to use it as before, but I wanted to eliminate the need for unique classes for each template I create. Many panels have up to 30 to 40 templates and certainly some 20 to 30 tend to be different and need to be created 1 by one.. the way I am creating it now, only the data that is saved when browsing statuses needs to be identified 1 by 1. I'll research and maybe go back to the root.

image