# Gauge's for Dashboard 2.0 made with ui\_template

**URL:** <https://discourse.nodered.org/t/gauges-for-dashboard-2-0-made-with-ui-template/85955>\
**Category:** Dashboard\
**Tags:** dashboard-2\
**Created:** [27 February 2024 13:43 UTC](https://discourse.nodered.org/t/gauges-for-dashboard-2-0-made-with-ui-template/85955 "2024-02-27T13:43:09Z")\
**Posts on this page:** 1\
**Showing post:** 6

<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:** [5 March 2024 07:19 UTC](https://discourse.nodered.org/t/gauges-for-dashboard-2-0-made-with-ui-template/85955/6 "2024-03-05T07:19:47Z")

</div>

Couldn't sleep this night too well, spent couple of night hours...

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

> **Code is HERE**
>
> ```auto
> <script>
> export default{
> data(){
> return {
> //define me here
> min:0,
> max:100,
> label:"Gauge",
> measurement:"Temperature",
> unit:"°C",
> majorDivision:10, // number of input units for each (numbered) major division
> minorDivision: 2, // number of input units for each minor division
> sectors:[{start:0,end:10,color:"skyblue"},{start:70,end:90,color:"#e99200"},{start:90,end:100,color:"red"}],
> valueDecimalPlaces: 1,
> majorDecimalPlaces:0,
> allowOverflow:true, // needle can travel over scale boundaries
> showUpdate:true,
> updateLabel:"Last update:",               
>                 
> //no need to change
> value:0,
> updateTime:0               
> }
> }
> }
> </script>
> <template>
> <div class="hn-sng">
> <div class="label">{{label}}</div>
> <svg ref="hn-gauge" width="100%" height="100%" viewBox="0 0 100 100">
> <g>
> <path v-for="(item, index) in sectors" :key="index" :ref="'sector-' + index" class="sector"
> stroke-width="5" d="M 10 90 A 47.5 47.5 25 1 1 90 90"></path>
> </g>
> <g>
> <path class="tick-minor" :style="{'stroke-dasharray':minor }" pathLength="492" stroke-width="5" d="M 10 90 A 47.5 47.5 25 1 1 90 90" ></path>
> <path ref="arc" class="tick-major" :style="{'stroke-dasharray':major }" pathLength="246" stroke-width="5" d="M 10 90 A 47.5 47.5 25 1 1 90 90"></path>
> </g>
> <g>
> <text v-for="(item, index) in numbers" :key="index" class="num" text-anchor="middle" y="-37"
> :style="`rotate: ${item.r}deg;`">{{item.n}}</text>
> </g>
> <g>
> <text class="measurement" y="48" x="50%" text-anchor="middle">{{measurement}}</text>
> <text class="unit" y="75" x="50%" text-anchor="middle">{{unit}}</text>
> <text class="value" y="90" x="50%" text-anchor="middle">{{formattedValue}}</text>
> <text v-if="showUpdate" ref="update" class="update" y="100" x="0"
> text-anchor="left">{{formattedUpdate}}</text>
> </g>
> <g ref="o-needle" class="o-needle">
> <path d="M 0,0 -1.5,0 -0.25,-43 0.25,-43 1.5,0 z"></path>
> <circle cx="0" cy="0" r="3"></circle>
> </g>
> </svg>
> </div>
> </template>
> 
> <script>
> export default{ 
> methods:{
> getElement: function(name,base){
> if(base){
> return this.$refs[name]
> }
> return this.$refs[name][0]
> },
> validate: function(data){
> let ret                
> if(typeof data !== "number"){
> ret = parseFloat(data)
> if(isNaN(ret)){
> console.log("BAD DATA! gauge id:",this.id,"data:",data)
> return null
> }
> }                    
> else{
> ret = data
> }                
> return ret
> },
> range:function (n, p, r) {           
> if (p.maxIn > p.minIn) {
> n = Math.min(n, p.maxIn)
> n = Math.max(n, p.minIn)
> } else {
> n = Math.min(n, p.minIn)
> n = Math.max(n, p.maxIn)
> }
> if(r){
> return Math.round(((n - p.minIn) / (p.maxIn - p.minIn) * (p.maxOut - p.minOut)) + p.minOut);
> }
> return ((n - p.minIn) / (p.maxIn - p.minIn) * (p.maxOut - p.minOut)) + p.minOut;
> },
> generateNumbers:function(min,max){               
>                
> let nums = []
> let t = (max - min) / this.divisors.major;
> let i = min;
> let ro = -123
> let rp = 246 / this.divisors.major
>                
> for (let e = 0; e < this.divisors.major + 1; ++e){
> if(this.min > this.max){
> nums.unshift({n: i.toFixed(this.majorDecimalPlaces),r: ro + (e * rp) })
> }
> else{
> nums.push({n: i.toFixed(this.majorDecimalPlaces),r: ro + (e * rp) })
> }
>                     
> i = parseFloat(i + t);                   
> }
> return nums 
> },
> sectorData:function(full){  
> 
> let ret = []
> this.sectors.forEach((sector,idx) => {
> let sec = {name:'sector-'+idx,color:sector.color}
> const params = {minIn:this.min, maxIn:this.max, minOut:0, maxOut:full}
> const start = this.range(sector.start,params,false)
> const end = this.range(sector.end,params,false)
> const pos = Math.min(start, end)
> const span = Math.max(start, end) - pos
> sec.css = `0 ${pos} ${span} var(--dash)`
> ret.push(sec)
> })
> return ret
> },
> rotation:function(v){
> const overflow = this.allowOverflow ? (this.max-this.min) * 0.1 : 0
> const maxRotation = this.allowOverflow ? (123 * 1.2) : 123
> const min = this.min - overflow
> const max = this.max + overflow
> const params = {minIn:min, maxIn:max, minOut:-maxRotation, maxOut:maxRotation};
> if (v === null) {
> v = Math.min(min, max)
> }
> return this.range(v,params,false) +'deg'          
> }
> 
> },
> watch: {
> msg: function(){
> if(this.msg.payload != undefined){
> this.value = this.validate(this.msg.payload)
> this.updateTime = Date.now()                    
> this.getElement('o-needle',true).style.rotate = this.rotation(this.value)                    
> }
> }
> },
> computed: {            
> formattedValue: function () {
> return this.value !== null ? this.value.toFixed(this.valueDecimalPlaces) : "---"
> },
> formattedUpdate:function (){
> return this.updateLabel+" "+this.time
> },
> time:function(){
> return new Intl.DateTimeFormat('default', {hour: 'numeric', minute: 'numeric',second: 'numeric'}).format(this.updateTime);
> },
> numbers:function(){
> return this.generateNumbers(this.min,this.max)
> },
> major:function(){
> return "2," + ((246 / this.divisors.major)-2)           
> },
> minor:function(){
> let step = 492 / this.divisors.major / this.divisors.minor
> let line = "0," + step + ","
> step --
> for(let i=0;i<this.divisors.minor-1;++i){
> line += "1,"+step+","
> }
> line = line.slice(0, -1);
> 
> return line
> },
> offset:function(){
> return -1
> },
> divisors:function(){
> let span
> if(this.max > this.min){
> span = this.max - this.min
> }
> else{
> span = this.min - this.max                
> }
> const mad = this.majorDivision <= 0 ? 1 : this.majorDivision;
> const mid = this.minorDivision <= 0 ? 1 : this.minorDivision;
> const ma = span / mad
> const mi = span / ma / mid
> return {major:ma,minor:mi}
> }
> 
> },
> mounted(){
>            
> const dal = this.getElement('arc',true).getTotalLength()
> const sec = this.sectorData(dal)              
> const gauge = this.getElement('hn-gauge',true)
> gauge.style.setProperty('--dash',dal)
>            
> sec.forEach(s =>{
> const sector = this.getElement(s.name,false)
> sector.style.setProperty("stroke-dasharray",s.css)
> sector.style.setProperty("stroke",s.color)
> })
> this.getElement('o-needle',true).style.rotate = this.rotation(null)
> this.$nextTick(() => {
> this.getElement('o-needle',true).style.transition= "rotate .5s"
> })
> }
> }
> </script>
> <style>
> .hn-sng {
> position: relative;
> user-select: none;
> }
> 
> .hn-sng .label {
> position: absolute;
> font-size: 1rem;
> color: currentColor;
> text-align: center;
> width: 100%;
> overflow: hidden;
> white-space: nowrap;
> text-overflow: ellipsis;
> }
> 
> .hn-sng .value {
> fill: currentColor;
> font-weigth: 600;
> }
> 
> .hn-sng .unit {
> fill: currentColor;
> font-size: 0.4rem;
> }
> 
> .hn-sng .measurement {
> fill: currentColor;
> font-size: 0.5rem;
> }
> 
> .hn-sng .update {
> fill: currentColor;
> fill-opacity: 0.6;
> font-size: 0.35rem;
> }
> 
> .hn-sng .num {
> transform-origin: center 64.15%;
> transform: translate(50%, 64.15%);
> fill: currentColor;
> fill-opacity: 0.6;
> font-size: .35rem;
> }
> 
> .hn-sng .tick-minor {       
> stroke-dashoffset: 0;
> fill: none;
> stroke: currentColor;
> stroke-opacity: 0.6;
> }
> 
> .hn-sng .tick-major {
> stroke-dashoffset: 0.75;
> fill: none;
> stroke: currentColor;
> }
> 
> .hn-sng .sector {
> fill: none;
> stroke: transparent;
> }
> .hn-sng .sector:first-of-type {
> stroke-dashoffset:-0.5;
> }
> 
> .hn-sng .o-needle {
> transform-origin: center 64.15%;
> transform: translate(50%, 64.15%);
> transition: unset;
> }
> 
> .hn-sng .o-needle path,
> .hn-sng .o-needle circle {
> fill: currentColor;
> }
> </style>
> 
> ```

---

_[View the full topic](https://discourse.nodered.org/t/gauges-for-dashboard-2-0-made-with-ui-template/85955)._
