# Dashboard 2.0 is now Generally Available

**URL:** <https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913>\
**Category:** Dashboard\
**Tags:** dashboard-2\
**Created:** [25 January 2024 16:59 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913 "2024-01-25T16:59:41Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![joepavitt](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/joepavitt/32/59722_2.png) [@joepavitt](https://discourse.nodered.org/u/joepavitt)\
**Post date:** [26 January 2024 10:39 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/21 "2024-01-26T10:39:05Z")

</div>

> [@Colin](#):
>
> **[Edit]** Having tested, if the PC is suspended and changes are made to, for example, Switch positions, then when the PC is resumed the dashboard does not show those changes, I suspect the same will happen if the browser suspends a tab. I think this is a pretty serious issue.

I suspect the piece I'm missing here is a differentiation between `on-load` and `on-connect`. The former is running smoothly, when a browser loads, refreshes, etc. The latter is what I would _expect_ the SocketIO client to do automatically, but it would appear that it isn't.

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [26 January 2024 11:06 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/22 "2024-01-26T11:06:13Z")

</div>

> [@joepavitt](#):
>
> The latter is what I would _expect_ the SocketIO client to do automatically, but it would appear that it isn't.

[Socket.IO](http://Socket.IO) does not cache any data, you have to do that yourself in Node-RED. I think that D1 did this internally in some "magic" function. in UIBUILDER, I avoided the problem and maintained flexibility by having a separate cache node and making the main node output suitable control messages to trigger the cache.

However, the latest version of [Socket.IO](http://Socket.IO) DOES have a caching capability. Not sure how good it is though and not sure what the impact might be if an environment had tons of complex messages with lots of offline users.

---

<div class="post-metadata">

**Author:** ![diegodamaceno](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/diegodamaceno/32/85742_2.png) [@diegodamaceno](https://discourse.nodered.org/u/diegodamaceno)\
**Post date:** [26 January 2024 15:09 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/23 "2024-01-26T15:09:56Z")

</div>

@joepavitt @TotallyInformation @Colin

I believe we are talking about the same thing when I point out that the ui-templat node, when it receives a simple button project, does not maintain its state when the page is reloaded or updated. Everything I set up within ui-templat returns to its origin when I restart the system or simply reload the page. D1 maintained this, I don't know exactly how. I would like to learn how to maintain the state. Below is an example of a button.

```auto
<template>
    <v-card ref="card"
        style="display: flex; flex-direction: column; margin: auto; width: 75px; height: 105px; background-color: #4F4F4F; border: 1px solid #000000; border-radius: 18px;">
        <v-btn ref="botao"
            style="width: 75px; height: 75px; color: #000000; background-color: #4F4F4F; font-size: 14px; border-radius: 18px;"
            stacked @click="alternar">
            Luz
            <v-icon ref="icon" style="font-size: 45px;">mdi-lightbulb-variant-outline</v-icon>
        </v-btn>
        <v-slider ref="barra" color="#BD9608" v-model="barravalor" @end="deslisamento"></v-slider>
    </v-card>
</template>

<script>
    export default {
        data() {
            return {
                chave: 'a1',
                barravalor: 0
            };
        },
        methods: {
            alternar: function () {
                if (window.localStorage.getItem(this.chave) === 'ON') {
                    this.desligar();                    
                } else if (window.localStorage.getItem(this.chave) === 'OFF') {
                    this.ligar();                    
                } 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;
                if (window.localStorage.getItem(this.chave) === 'OFF') {
                    this.send({ status: 'ON' });           
                }
                
            },
            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;
                if (window.localStorage.getItem(this.chave) === 'ON') {
                    this.send({ status: 'OFF' });           
                }
                
            },
            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>

```

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [26 January 2024 15:28 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/24 "2024-01-26T15:28:20Z")

</div>

> [@diegodamaceno](#):
>
> I believe we are talking about the same thing when I point out that the ui-templat node, when it receives a simple button project, does not maintain its state when the page is reloaded or updated.

Not exactly, I am talking about the core widgets such as switches, text etc, which do retain their state over a reload. The situation is, I believe, different for a template node.

---

<div class="post-metadata">

**Author:** ![davidz](https://avatars.discourse-cdn.com/v4/letter/d/a88e57/32.png) [@davidz](https://discourse.nodered.org/u/davidz)\
**Post date:** [27 January 2024 16:49 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/25 "2024-01-27T16:49:45Z")

</div>

We plan to build a GUI for a new product based on Dashboard 2.0. This GUI will be tested many times before release.

There may be a minor bug on the Dashboard 2.0 (v1.0 and v1.0.2).: The landing page does not change with the order of pages Each time when users go the the dashboard, it always land on the "Gateway config" page (circled in red)

 ![Screenshot 2024-01-27 084409](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/4/4/449da96317846d5c32bca977394a93c4cab5ae88.jpeg)  
It is supposed to land on the "Home" page instead.

---

<div class="post-metadata">

**Author:** ![joepavitt](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/joepavitt/32/59722_2.png) [@joepavitt](https://discourse.nodered.org/u/joepavitt)\
**Post date:** [27 January 2024 17:52 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/26 "2024-01-27T17:52:23Z")

</div>

Hi @davidz its a missing feeature at the moment: [Configure Default Page · Issue #137 · FlowFuse/node-red-dashboard · GitHub](https://github.com/FlowFuse/node-red-dashboard/issues/137)

Hopefully we will get it in soon

---

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [27 January 2024 18:10 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/27 "2024-01-27T18:10:04Z")

</div>

I made some changes to the labels of the ui-text nodes. I could not figure out why the labels did not change after deploy. I had to restart the flow to make the changes happen

---

<div class="post-metadata">

**Author:** ![joepavitt](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/joepavitt/32/59722_2.png) [@joepavitt](https://discourse.nodered.org/u/joepavitt)\
**Post date:** [27 January 2024 18:38 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/28 "2024-01-27T18:38:33Z")

</div>

If you can reproduce the problem again, can you please open an issue here: [GitHub - FlowFuse/node-red-dashboard](https://github.com/FlowFuse/node-red-dashboard)

---

<div class="post-metadata">

**Author:** ![davidz](https://avatars.discourse-cdn.com/v4/letter/d/a88e57/32.png) [@davidz](https://discourse.nodered.org/u/davidz)\
**Post date:** [27 January 2024 19:38 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/29 "2024-01-27T19:38:52Z")

</div>

@joepavitt Got it. Thank you!

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [29 January 2024 10:20 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/30 "2024-01-29T10:20:31Z")

</div>

Quick one for monday morning to play with ...

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/d/5/d536e822e0556b13b78c5e6887e1718de319af08.png)

Template

```auto
<template>
    <div class="ag-wrapper">
        <div class="ag-icon">
            <v-icon aria-hidden="false">{{icon}}</v-icon>
        </div>
        <div class="ag-content">
            <div class="ag-text">
                <span class="ag-label">{{label}}</span>
                <span class="ag-value">{{formattedValue}}<span class="ag-unit">{{unit}}</span></span>
            </div>
            <div class="ag-track">
                <div class="ag-track-background"></div>            
                <div class="ag-track-foreground" :style="{'width': percentage +'%'}"></div>
            </div>
            <div class="ag-limits">
                <span class="ag-min">{{min}}</span>
                <span class="ag-max">{{max}}</span>
            </div>
        </div>
    </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                // define me here               
                label:"MEASURE",
                unit:"Kg",
                icon:"mdi-account",               
                min:0,
                max:100,

                //no need to change those
                value: 0
            }
        },
        watch: {
            msg: function(){
                if(this.msg.payload != undefined){                   
                    this.value = this.msg.payload                    
                }
            }
        },
        computed: {
            formattedValue: function () {
                return this.value.toFixed(2)
            },
            percentage: function(){
                return Math.floor(((this.value - this.min) / (this.max - this.min)) * 100);
            } 
        }
    }
</script>

```

CSS

```auto
.ag-wrapper {
    display: grid;
    grid-template-columns: 3em 1fr;
    gap:1em;
}
.ag-icon{
    font-size: 2em;
    display: flex;
    flex-direction: column;
    justify-content: center;        
}
.ag-content{
    display: grid;
    grid-template-rows: 1fr 7px 0.75em;
    gap: 2px;
}
.ag-text{
    font-size: 1.25em;
    line-height: 1em;
    align-self: end;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
}
.ag-value{       
    font-weight:bold;
}
.ag-unit{
    font-size:.75em;
    font-weight:normal;
    padding-inline-start: 0.15em;
}
.ag-limits{
    display: flex;
    justify-content: space-between;
    font-size: .75em;
    line-height: .75em;
    align-content: center;
    flex-wrap: wrap;
}

.ag-track{
    position:relative;
    display:flex;
    align-items: center;
    width: 100%;
    border-radius: 6px;
}

.ag-track-background{
    position:absolute;
    background: rgb(var(--v-theme-primary));
    opacity: 0.45;
    width: 100%;
    height: 50%;
    border-radius:inherit;
}
.ag-track-foreground{
    position:absolute;
    background: rgb(var(--v-theme-primary));       
    width: 50%;
    height: 100%;
    border-radius:inherit;
}

```

---

<div class="post-metadata">

**Author:** ![diegodamaceno](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/diegodamaceno/32/85742_2.png) [@diegodamaceno](https://discourse.nodered.org/u/diegodamaceno)\
**Post date:** [29 January 2024 14:40 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/31 "2024-01-29T14:40:59Z")

</div>

I'm using Google Chome and the bar only shows the value updating when I refresh the page. This approach was great and I would like to understand more about how you are maintaining the value when updating the page.

I used this approach

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/2/3/239946e8b7500c006c562f28fab9da82e6526ed0.png)

```auto
<template>
    <v-progress-circular size="20" style="display: flex; margin: auto; width: 75px; height: 75px; color: #BD9608; background-color: #4F4F4F; border: 1px solid #000000; border-radius: 18px;" v-model="entrada">
        <template v-slot:default="{ value }">
            <strong style="color: #000000">{{ Math.ceil(value) }}%</strong>
        </template>
    </v-progress-circular>
</template>

<script>
  export default {
    data() {
      return {
        chave: 'a5',
        entrada: 0,
      };
    },
    methods: {
      setState(value) {
        this.entrada = value;
        localStorage.setItem(this.chave, value.toString());
    },
    recuperarEstadoAnterior() {
      const savedValue = localStorage.getItem(this.chave);
        if (savedValue !== null) {
          this.entrada = parseInt(savedValue, 10);
        }
      },
    },
    mounted() {
      this.recuperarEstadoAnterior();
      this.$socket.on("msg-input:" + this.id, (msg) => {
        this.setState(msg.payload);
      });
    },
  };
</script>

```

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [29 January 2024 15:12 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/32 "2024-01-29T15:12:24Z")

</div>

> [@hotNipi](#):
>
> ```auto
> watch: {
> msg: function(){
> if(this.msg.payload != undefined){                   
> this.value = this.msg.payload                    
> }
> }
> }
> 
> ```

I'm far from being an expert on Vue stuff, but as it is possible to watch changes and the msg is also possible to watch, then whenever the msg comes in, the change is reflected.

```auto
mounted() {
      this.recuperarEstadoAnterior();
      this.$socket.on("msg-input:" + this.id, (msg) => {
        this.setState(msg.payload);
      });
    }

```

By registering the socket listener on mounted, I think it may be too late and you may be missing first message which has last known good value.

But as told - I don't know even if I'm doing it right way ...

---

<div class="post-metadata">

**Author:** ![diegodamaceno](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/diegodamaceno/32/85742_2.png) [@diegodamaceno](https://discourse.nodered.org/u/diegodamaceno)\
**Post date:** [29 January 2024 15:19 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/33 "2024-01-29T15:19:22Z")

</div>

Surely I must have a problem here. I'm taking your code without changing anything and when I send the value to the template it doesn't change. To see the new value I sent, I need to refresh the page. =[

---

<div class="post-metadata">

**Author:** ![joepavitt](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/joepavitt/32/59722_2.png) [@joepavitt](https://discourse.nodered.org/u/joepavitt)\
**Post date:** [29 January 2024 15:26 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/34 "2024-01-29T15:26:27Z")

</div>

This is actually a really nice way to do it, away on holidsy this week, but I'll update the docs when I'm back with this

---

<div class="post-metadata">

**Author:** ![diegodamaceno](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/diegodamaceno/32/85742_2.png) [@diegodamaceno](https://discourse.nodered.org/u/diegodamaceno)\
**Post date:** [29 January 2024 15:55 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/35 "2024-01-29T15:55:32Z")

</div>

I had already reached this goal previously, but every time the page is updated, the button is lost and does not tell me whether it is on or off.

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/6/b/6b38e5ecbe85016762c5240a9b41704d83322a07.png)

```auto
<template>
    <v-btn ref="botao"
        style="display: flex; flex-direction: column; margin: auto; height: 75px; width: 75px; background-color: #4F4F4F; color: #000000; border: 1px solid #000000; font-size: 14px; border-radius: 18px;"
        stacked @click="alternar">
        Externa
        <v-icon ref="icone" style="font-size: 40px;">mdi-beach</v-icon>
    </v-btn>
</template>

<script>
    export default {
        data() {
            return {
                value: "OFF"
            };
        },
        methods: {
            alternar: function () {
                if (this.value === 'ON') {
                    this.desligar();
                } else if (this.value === 'OFF') {
                    this.ligar();
                } else {
                    this.desligar();
                }
            },
            ligar: function () {
                this.$refs.icone.$el.style.color = '#BD9608';
                this.$refs.icone.$el.style.textShadow = '0px 0px 10px #BD9608';
                if (this.value === 'OFF') {
                    this.send({ status: 'ON' });
                    this.value = "ON"; 
                }
            },
            desligar: function () {
                this.$refs.icone.$el.style.color = '#A9A9A9';
                this.$refs.icone.$el.style.textShadow = '0px 0px 0px';
                if (this.value === 'ON') {
                    this.send({ status: 'OFF' });
                    this.value = "OFF"; 
                }
            }
        },
        watch: {
            msg: function(){
                if(this.msg.payload != undefined){
                    if (this.msg.payload === "ON") {
                        this.ligar();
                        this.value = "ON"; 
                    } else if (this.msg.payload === "OFF") {
                        this.desligar();
                        this.value = "OFF"; 
                    }
                }
            }
        }
    }
</script>

```

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [29 January 2024 16:00 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/36 "2024-01-29T16:00:33Z")

</div>

> [@diegodamaceno](#):
>
> page is updated

What do you mean by that?

---

<div class="post-metadata">

**Author:** ![diegodamaceno](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/diegodamaceno/32/85742_2.png) [@diegodamaceno](https://discourse.nodered.org/u/diegodamaceno)\
**Post date:** [29 January 2024 16:05 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/37 "2024-01-29T16:05:41Z")

</div>

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/b/9/b910cfe3a12b069037b3cc5d30cfd5c15959a4ee.png)

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [29 January 2024 16:31 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/38 "2024-01-29T16:31:37Z")

</div>

Maybe this way it works.

```auto
<template>
    <v-btn ref="botao"
        style="display: flex; flex-direction: column; margin: auto; height: 75px; width: 75px; background-color: #4F4F4F; color: #000000; border: 1px solid #000000; font-size: 14px; border-radius: 18px;"
        stacked @click="alternar">
        Externa
        <v-icon ref="icone" style="font-size: 40px;">mdi-beach</v-icon>
    </v-btn>
</template>

<script>
    export default {
        data() {
            return {
                value: "OFF"
            };
        },
        methods: {
            alternar: function () {
                if (this.value === 'ON') {
                    this.desligar();
                } else if (this.value === 'OFF') {
                    this.ligar();
                } else {
                    this.desligar();
                }
            },
            ligar: function () {
                this.$refs.icone.$el.style.color = '#BD9608';
                this.$refs.icone.$el.style.textShadow = '0px 0px 10px #BD9608';
                if (this.value === 'OFF') {
                    this.send({ payload: 'ON' });
                    this.value = "ON"; 
                }
            },
            desligar: function () {
                this.$refs.icone.$el.style.color = '#A9A9A9';
                this.$refs.icone.$el.style.textShadow = '0px 0px 0px';
                if (this.value === 'ON') {
                    this.send({ payload: 'OFF' });
                    this.value = "OFF"; 
                }
            }
        },
        watch: {
            msg: function(){
                if(this.msg.payload != undefined){
                    console.log('got message :',this.msg)
                    if (this.msg.payload === "ON") {
                        this.ligar();
                    } else if (this.msg.payload === "OFF") {
                        this.desligar();                        
                    }
                }
            }
        }
    }
</script>

```

---

<div class="post-metadata">

**Author:** ![diegodamaceno](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/diegodamaceno/32/85742_2.png) [@diegodamaceno](https://discourse.nodered.org/u/diegodamaceno)\
**Post date:** [29 January 2024 16:38 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/39 "2024-01-29T16:38:58Z")

</div>

It worked out! Was that the change?

```auto
console.log('got message :',this.msg)

```

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [29 January 2024 16:50 UTC](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913/40 "2024-01-29T16:50:15Z")

</div>

You had send function without payload. That was main thing why reply message wasn't heard.

[Previous page](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913.md?page=1)

[Next page](https://discourse.nodered.org/t/dashboard-2-0-is-now-generally-available/84913.md?page=3)
