I am trying to create my own button-toggle component with an html-template-node.The logic works so far. However, I can't get it to work with icons. They are not rendered for me.The icons work fine in a standard Dashboard 2.0 button-group-node.How do I integrate them correctly into the html-template-node?
Example
[
{
"id": "155d7a767c816aa9",
"type": "inject",
"z": "23f984cab29f3522",
"name": "Set Initial States",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "{\"power\":true,\"light\":false,\"fan\":false,\"settings\":true}",
"payloadType": "json",
"x": 90,
"y": 140,
"wires": [
[
"d8f5ad2451889c9a"
]
]
},
{
"id": "d8f5ad2451889c9a",
"type": "ui-template",
"z": "23f984cab29f3522",
"group": "ui-group-1",
"page": "",
"ui": "",
"name": "Toggle Button Group",
"order": 1,
"width": "6",
"height": "3",
"head": "<link href=\"https://fonts.googleapis.com/icon?family=Material+Icons\" rel=\"stylesheet\">",
"format": "<template>\n <div class=\"toggle-widget-container\">\n <div :style=\"containerStyles\" class=\"toggle-button-group\">\n <div \n v-for=\"(button, index) in buttons\" \n :key=\"button.name\"\n :class=\"['toggle-btn', { \n 'active': toggleStates[button.name], \n 'first': index === 0, \n 'last': index === buttons.length - 1 \n }]\"\n :style=\"getButtonStyle(button.name, index)\"\n @click=\"toggleButton(button.name)\"\n @mouseenter=\"showTooltip(button.name, $event)\"\n @mouseleave=\"hideTooltip\"\n :title=\"button.tooltip\"\n >\n <i :class=\"getIconClass(button.icon)\" class=\"material-icons\">{{ getIconName(button.icon) }}</i>\n </div>\n </div>\n \n <div v-if=\"tooltipVisible\" class=\"tooltip\" :style=\"tooltipStyle\">\n {{ getTooltipText(tooltipVisible) }}\n </div>\n </div>\n</template>\n\n\n<script>\nexport default {\n data() {\n return {\n toggleStates: {\n 'power': false,\n 'light': false,\n 'fan': false,\n 'settings': false\n },\n config: {\n border: 1,\n size: '35px',\n position: 'bottom-right',\n distance: 5,\n color: {\n active: '#2196F3',\n inactive: '#FFFFFF',\n hover: '#BBDEFB',\n border: '#E0E0E0'\n }\n },\n buttons: [\n { name: 'config', icon: 'hammer-screwdriver', tooltip: 'cinfig' },\n { name: 'charts', icon: 'chart-line', tooltip: 'charts' },\n { name: 'temperature', icon: 'temperature-celsius', tooltip: 'Temperature Control' },\n { name: 'humidifier', icon: 'air-humidifier', tooltip: 'humidifier Control' }\n ],\n tooltipVisible: null,\n tooltipStyle: {}\n };\n },\n computed: {\n containerStyles() {\n const pos = this.config.position || 'bottom-right';\n const distance = `${this.config.distance || 5}px`;\n \n let positioning = {};\n \n if (pos.includes('bottom')) {\n positioning.bottom = distance;\n } else if (pos.includes('top')) {\n positioning.top = distance;\n }\n \n if (pos.includes('right')) {\n positioning.right = distance;\n } else if (pos.includes('left')) {\n positioning.left = distance;\n }\n \n return {\n position: 'absolute',\n display: 'flex',\n zIndex: 1000,\n ...positioning\n };\n }\n },\n methods: {\n toggleButton(buttonName) {\n const newState = !this.toggleStates[buttonName];\n this.toggleStates[buttonName] = newState;\n \n // Send message to Node-RED\n this.send({\n payload: {\n [buttonName]: newState\n },\n timestamp: Date.now()\n });\n },\n \n getButtonStyle(buttonName, index) {\n const size = this.config.size || '35px';\n const border = this.config.border || 1;\n const colors = this.config.color || {\n active: '#2196F3',\n inactive: '#FFFFFF',\n hover: '#BBDEFB',\n border: '#E0E0E0'\n };\n \n const isActive = this.toggleStates[buttonName];\n const isFirst = index === 0;\n const isLast = index === this.buttons.length - 1;\n \n let backgroundColor = colors.inactive;\n let textColor = '#666';\n \n if (isActive) {\n backgroundColor = colors.active;\n textColor = 'white';\n }\n \n return {\n width: size,\n height: size,\n backgroundColor: backgroundColor,\n color: textColor,\n border: `${border}px solid ${colors.border}`,\n borderRight: isLast ? `${border}px solid ${colors.border}` : 'none',\n borderRadius: '0',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n userSelect: 'none',\n boxSizing: 'border-box'\n };\n },\n \n getIconClass(icon) {\n return 'material-icons';\n },\n \n getIconName(icon) {\n return icon;\n },\n \n getTooltipText(buttonName) {\n const button = this.buttons.find(b => b.name === buttonName);\n return button ? button.tooltip : '';\n },\n \n showTooltip(buttonName, event) {\n this.tooltipVisible = buttonName;\n \n const rect = event.target.getBoundingClientRect();\n this.tooltipStyle = {\n position: 'fixed',\n top: (rect.top - 35) + 'px',\n left: (rect.left + rect.width / 2) + 'px',\n transform: 'translateX(-50%)',\n backgroundColor: 'rgba(0, 0, 0, 0.8)',\n color: 'white',\n padding: '6px 10px',\n borderRadius: '4px',\n fontSize: '12px',\n whiteSpace: 'nowrap',\n zIndex: 10001,\n pointerEvents: 'none'\n };\n },\n \n hideTooltip() {\n this.tooltipVisible = null;\n }\n },\n \n watch: {\n msg: {\n handler(newMsg) {\n if (newMsg && newMsg.payload) {\n // Update states from incoming message\n Object.assign(this.toggleStates, newMsg.payload);\n }\n \n if (newMsg && newMsg.config) {\n // Update configuration from incoming message\n Object.assign(this.config, newMsg.config);\n }\n \n if (newMsg && newMsg.buttons) {\n // Update button definitions from incoming message\n this.buttons = newMsg.buttons;\n \n // Initialize states for new buttons\n newMsg.buttons.forEach(button => {\n if (!(button.name in this.toggleStates)) {\n this.$set(this.toggleStates, button.name, false);\n }\n });\n }\n },\n deep: true\n }\n }\n}\n</script>\n\n<style scoped>\n.toggle-widget-container {\n position: relative;\n height: 100%;\n width: 100%;\n}\n\n.toggle-button-group {\n display: flex;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n.toggle-btn {\n transition: all 0.2s ease;\n}\n\n.toggle-btn:hover {\n background-color: #BBDEFB !important;\n z-index: 1;\n}\n\n.toggle-btn.active:hover {\n background-color: #1976D2 !important;\n}\n\n.tooltip {\n position: fixed;\n background-color: rgba(0, 0, 0, 0.8);\n color: white;\n padding: 6px 10px;\n border-radius: 4px;\n font-size: 12px;\n white-space: nowrap;\n z-index: 10001;\n pointer-events: none;\n}\n\n.material-icons {\n font-size: 18px;\n line-height: 1;\n}\n</style>",
"storeOutMessages": true,
"passthru": false,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 300,
"y": 200,
"wires": [
[
"422345191feb1813"
]
]
},
{
"id": "422345191feb1813",
"type": "debug",
"z": "23f984cab29f3522",
"name": "Toggle Output",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 520,
"y": 200,
"wires": []
},
{
"id": "ui-group-1",
"type": "ui-group",
"name": "Toggle Button Group",
"page": "ui-page-1",
"width": "6",
"height": "1",
"order": 1,
"showTitle": true,
"className": "",
"visible": true,
"disabled": false
},
{
"id": "ui-page-1",
"type": "ui-page",
"name": "Test Page",
"ui": "1f09dc2ae6515591",
"path": "/toggle-test",
"icon": "home",
"layout": "grid",
"theme": "ui-theme-1",
"order": 2,
"className": "",
"visible": true,
"disabled": false
},
{
"id": "1f09dc2ae6515591",
"type": "ui-base",
"name": "My Dashboard 2.0",
"path": "/dashboard",
"appIcon": "",
"includeClientData": true,
"acceptsClientConfig": [
"ui-notification",
"ui-control"
],
"showPathInSidebar": false,
"headerContent": "page",
"navigationStyle": "temporary",
"titleBarStyle": "default",
"showReconnectNotification": true,
"notificationDisplayTime": 1,
"showDisconnectNotification": true,
"allowInstall": true
},
{
"id": "ui-theme-1",
"type": "ui-theme",
"name": "Default Theme",
"colors": {
"surface": "#ffffff",
"primary": "#0094ce",
"bgPage": "#eeeeee",
"groupBg": "#ffffff",
"groupOutline": "#cccccc"
},
"sizes": {
"pagePadding": "12px",
"groupGap": "12px",
"groupBorderRadius": "4px",
"widgetGap": "12px"
}
},
{
"id": "75ec18fe22e66bed",
"type": "global-config",
"env": [],
"modules": {
"@flowfuse/node-red-dashboard": "1.26.0"
}
}
]