Dashboard2 ColorPicker Vuetify - Why the lack of <script> magic?

Hi All

I'll be upfront and say that I got very frustrated with DashBoard2 when it came time to migrate colour pickers.

The template examples on this page look AMAZING and I was really looking forward to using them

The API for them seems to present SO many ways to tweak the layout too.
https://vuetifyjs.com/en/api/v-color-picker/

BUT

That's where the fun stopped and the real pain started

"How to get the selected colours back into Node-red"

Fair play to all those that offered help in different topics, but I did feel a lot like the "stupid friend who didn't know the secret language"

Lot's of great help and pointers, but no real world working examples.

As much as it grieves me, I just turned to Google and asked "node-red dashboard 2 color picker working example"

Which gave me a fairly simply reply

For a working color picker in Node-RED Dashboard 2.0 (FlowFuse), use a standard ui-template node utilizing Vuetify's built-in <v-color-picker>. Because the native template output relies on events, it is best practice to bind the picker to a data property and use a Vue watch to emit color changes back to Node-RED. [1, 2, 3, 4]

Step-by-Step Implementation

  1. Drag a ui-template node onto your canvas and double-click to edit it.
  2. Set the Group to your target UI Group and Page.
  3. Set the Format drop-down to Vue.
  4. Paste the following code into the Template text box: [1]

html

<template>
    <v-card class="mx-auto" max-width="300">
        <v-card-text class="d-flex justify-center">
            <v-color-picker
                v-model="color"
                dot-size="25"
                mode="hexa"
                show-swatches
            ></v-color-picker>
        </v-card-text>
    </v-card>
</template>

<script>
export default {
    data() {
        return {
            color: '#FF0000'
        }
    },
    watch: {
        color(newVal) {
            // Send the color value back to Node-RED on change
            this.send({ payload: newVal });
        }
    }
}
</script>

Use code with caution.

  1. Click Done and deploy your flow. The picker will send out the updated color code as a msg.payload string immediately as you drag the color wheel or select swatches

Thankfully this works and by changing the Template section, I can adapt to whatever layout I like.

Sadly, the issue still stands that I truly have no idea "WHY IT WORKS"....

I'm not seeking an explanation here, I just wanted to raise the subject that not everyone seeking help has a full working knowledge of required code.

As with anything, if you are helping someone, please give a little more than "This document has everything you need" (which I'm sure it does, IF you understand the basics in the background and the secret language)

Node-red examples

    {
        "id": "c4e5172f33950768",
        "type": "function",
        "z": "05226917997ae10c",
        "name": "Set Colour into a payload",
        "func": "// {\"colour\":\"#0000FF\"}\nvar colour = String(msg.payload) || \"#888888\";\nmsg.payload = {\"colour\":colour};\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 980,
        "wires": [
            [
                "261133621637a490"
            ]
        ]
    },
    {
        "id": "c5f6948951adac32",
        "type": "ui-text-input",
        "z": "05226917997ae10c",
        "group": "48fc19d1161f11e6",
        "name": "test colour",
        "label": "colour",
        "order": 0,
        "width": 0,
        "height": 0,
        "topic": "topic",
        "topicType": "msg",
        "mode": "color",
        "tooltip": "",
        "delay": 300,
        "passthru": true,
        "sendOnDelay": true,
        "sendOnBlur": true,
        "sendOnEnter": true,
        "className": "",
        "clearable": false,
        "sendOnClear": false,
        "icon": "",
        "iconPosition": "left",
        "iconInnerPosition": "inside",
        "x": 250,
        "y": 1060,
        "wires": [
            [
                "89ebe423685554b2"
            ]
        ]
    },
    {
        "id": "efbd69086911fce8",
        "type": "ui-template",
        "z": "05226917997ae10c",
        "group": "48fc19d1161f11e6",
        "page": "",
        "ui": "",
        "name": "CollourPicker 2",
        "order": 2,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n  <div class=\"d-flex justify-space-around\">\n    <v-color-picker\n      v-model=\"color\"\n      :modes=\"['rgba']\"\n    ></v-color-picker>\n\n    <div class=\"d-flex flex-column\">\n      <v-color-picker\n        v-model=\"color\"\n        v-model:mode=\"mode\"\n      ></v-color-picker>\n      <v-select\n        v-model=\"mode\"\n        :items=\"modes\"\n        style=\"max-width: 300px\"\n      ></v-select>\n    </div>\n  </div>\n</template>\n<script setup>\n  import { ref } from 'vue'\n\n  const color = ref('#ff00ff')\n  const mode = ref('hsla')\n  const modes = ref(['hsla', 'rgba', 'hexa'])\n</script>\n\n<script>\nexport default {\n    data() {\n        return {\n            color: '#FF0000'\n        }\n    },\n    watch: {\n        color(newVal) {\n            // Send the color value back to Node-RED on change\n            this.send({ payload: newVal });\n        }\n    }\n}\n</script>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 300,
        "y": 1100,
        "wires": [
            [
                "89ebe423685554b2"
            ]
        ]
    },
    {
        "id": "52b7b111fb88dd6e",
        "type": "ui-template",
        "z": "05226917997ae10c",
        "group": "48fc19d1161f11e6",
        "page": "",
        "ui": "",
        "name": "CollourPicker simple",
        "order": 1,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n    <v-card class=\"mx-auto\" max-width=\"300\">\n        <v-card-text class=\"d-flex justify-center\">\n            <v-color-picker\n                v-model=\"color\"\n                dot-size=\"25\"\n                mode=\"hexa\"\n                show-swatches\n            ></v-color-picker>\n        </v-card-text>\n    </v-card>\n</template>\n\n<script>\nexport default {\n    data() {\n        return {\n            color: '#FF0000'\n        }\n    },\n    watch: {\n        color(newVal) {\n            // Send the color value back to Node-RED on change\n            this.send({ payload: newVal });\n        }\n    }\n}\n</script>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 320,
        "y": 1140,
        "wires": [
            [
                "89ebe423685554b2"
            ]
        ]
    },
    {
        "id": "81d2f0e735d7b6da",
        "type": "ui-template",
        "z": "05226917997ae10c",
        "group": "48fc19d1161f11e6",
        "page": "",
        "ui": "",
        "name": "Brightness",
        "order": 3,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n    <div class=\"text-center\">\n        <v-rating v-model=\"rating\" active-color=\"blue\" color=\"orange-lighten-1\" length=\"10\">\n        </v-rating>\n    </div>\n</template>\n<script>\n    export default {\n        data: () => ({ rating: 3 }),\n        watch: {\n            rating: function () {\n                this.send({payload: this.rating})\n            }\n        }\n    }\n</script>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 330,
        "y": 1240,
        "wires": [
            [
                "3e876e2b77f80b0f"
            ]
        ]
    },
    {
        "id": "3e876e2b77f80b0f",
        "type": "debug",
        "z": "05226917997ae10c",
        "name": "DashBoard 2 Example vuetifyjs templates",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 820,
        "y": 1140,
        "wires": []
    },
    {
        "id": "89ebe423685554b2",
        "type": "junction",
        "z": "05226917997ae10c",
        "x": 460,
        "y": 1120,
        "wires": [
            [
                "3e876e2b77f80b0f",
                "c4e5172f33950768"
            ]
        ]
    },
    {
        "id": "48fc19d1161f11e6",
        "type": "ui-group",
        "name": "Test",
        "page": "19eb6d108e9275e2",
        "width": "20",
        "height": "13",
        "order": 1,
        "showTitle": true,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "19eb6d108e9275e2",
        "type": "ui-page",
        "name": "Examples",
        "ui": "485a6940f66045af",
        "path": "/examples",
        "icon": "",
        "layout": "tabs",
        "theme": "a965ccfef139317a",
        "order": 2,
        "className": "",
        "visible": true,
        "disabled": "false"
    },
    {
        "id": "485a6940f66045af",
        "type": "ui-base",
        "name": "Dashboard",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "headerContent": "page",
        "navigationStyle": "default",
        "titleBarStyle": "default",
        "showReconnectNotification": true,
        "notificationDisplayTime": 1,
        "showDisconnectNotification": true,
        "allowInstall": true
    },
    {
        "id": "a965ccfef139317a",
        "type": "ui-theme",
        "name": "Default",
        "colors": {
            "surface": "#ffffff",
            "primary": "#0094ce",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    },
    {
        "id": "153dad6ce93f598c",
        "type": "global-config",
        "env": [],
        "modules": {
            "@flowfuse/node-red-dashboard": "1.30.2"
        }
    }
]



Thanks to @Steve-Mcl for the extra tips

I've updated this Flow to add the Text entry box as a colour picker

It is a very difficult balance, if we were add every possible HTML widget type in the world (impossible - but even if we did), they would NOT be to everyone's taste - and that is what the template node is for.

However, when you get into this area, you need to at least understand a little bit about how HTML, JS and VUE works as well as the methods that push values to and from a frontend (your dashboard in the browser) to a server (node-red). Fortunately sending values back to node-red is reasonably well documented e.g. send from js fn, e.g. send on click, e.g. send on change however you still need a level of understanding.

So in your example you have

    data() {
        return {
            color: '#FF0000'  // *1
        }
    },
    watch: {
        color(newVal) { // *2
            // Send the color value back to Node-RED on change
            this.send({ payload: newVal }); // *3
        }
    }

*1 An entry in the data section generates a local variable named color. This is accessible in html/vue as color and accesable in js code as this.color

*2 this block adds a vue watch on the color variable and newVal will have the new value - it is "watched" and called every time color is changed. That is done in the template where you have v-model="color" (this is commen-a-garden, everyday VUE magic. The dashboard uses VUE so as stated before, a level of understanding is required!)

*3 as per the linked docs, this sends a msg with msg.payload set to the newVal value


PS, dashboard 2 text input has a colour mode (docs link)

Thanks Streve

I won't pretend I understand any of that, I am trying, it's just not in my wheel house.

If you ever want a building fully rewired with a complete digital backbone for heating, cooling, lighting, EV Charging, Solar generation blar blar blar.. give me a shout..

If you need someone to explain the code within each module that makes it work.... I'm not your man. :grinning_face:

WELL!!

I wish I'd found that MONTHS ago

That is SUPER easy and is what I'll be using going forward.

I can't imagine for a moment why I didn't think to look at the "text entry" node for a colour picker :laughing:

Careful what you say - I am looking into solar and battery (battery more so as I am on an electrickery tariff that is cheaper when there is excess energy - "octopus agile") and have no blimmin idea where to start!
I am capable (electrics, installation, etc all in my wheelhouse) - just no idea on available tech!

User name logged :smiley:

Seriously... I'm happy to help

Check this out for starters...

You might also hold back until a certain DIYSOS Episode airs at the of September....

(Southampton Episode)