This flow just includes 2 of the template nodes I was talking about.
If you copy the <style>
section from the Hourly chart over the <style>
section of the Daily chart you will (hopefully) notice a radical change. (Also note that class="base-container"
has been added to the first <div>
in the Daily template)
If you have several of your own template nodes on a page checkout the class data in the browser console
[{"id":"9e7e786081315c45","type":"ui-template","z":"fe7296cf2a5f7fba","group":"2a9d414817265ee2","page":"","ui":"","name":"Hourly Development","order":1,"width":"2","height":"1","head":"","format":"<template>\n <div>\n <div class = \"chart-title\">{{chartTitle}}</div> \n <div class = \"chart-container\"><canvas ref=\"chart\" /></div>\n\n </div> \n\n</template>\n\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n isChartLoaded: false,\n\n chartType: 'HBar',\n isStacked: false,\n \n chartTitle: 'Hourly Energy Useage',\n\n // Note that these are the reverse of a normal Bar chart\n yAxisKey: 'period', // String value - label, 'y' is the default\n xAxisKey: 'value', // Number value - data, 'x' is the default\n seriesKey: '', // '' is the default (none)\n\n xAxisMin: 0,\n xAxisMax: 0.5,\n\n yAxisTitle: '',\n xAxisTitle: '',\n\n isLegendDisplay: false,\n isAutoSkip: false,\n\n dataSets: [],\n \n }\n },\n\n watch: {\n msg: function () {\n\n if ( Object.hasOwn(this.msg, 'ui_update') ) {\n \n this.chartTitle = this.msg.ui_update?.chartTitle ?? this.chartTitle\n \n this.yAxisKey = this.msg.ui_update?.yAxisKey ?? this.yAxisKey\n this.xAxisKey = this.msg.ui_update?.xAxisKey ?? this.xAxisKey\n\n this.yAxisMax = this.msg.ui_update?.yAxisMax ?? this.yAxisMax\n this.xAxisMax = this.msg.ui_update?.xAxisMax ?? this.xAxisMax\n\n this.yAxisKey = this.msg.ui_update?.yAxisKey ?? this.yAxisKey\n this.xAxisKey = this.msg.ui_update?.xAxisKey ?? this.xAxisKey\n\n this.yAxisTitle = this.msg.ui_update?.yAxisTitle ?? this.yAxisTitle\n this.xAxisTitle = this.msg.ui_update?.xAxisTitle ?? this.xAxisTitle\n\n } \n\n if (this.isChartLoaded) {\n if (this.msg.payload !== undefined) this.onInput(this.msg)\n\n }\n \n }\n },\n \n computed: {\n backgroundColours: function() {\n let backgroundColour = [\n 'rgba(255, 99, 132, 1.0)',\n 'rgba(171, 199, 232, 1.0)',\n 'rgba(255, 127, 14, 1.0)',\n 'rgba(44, 160, 44, 1.0)',\n 'rgba(152, 223, 158, 1.0)',\n 'rgba(214, 39, 40, 1.0)',\n 'rgba(255, 155, 150, 1.0)',\n ]\n\n return backgroundColour\n \n },\n \n borderColours: function() {\n let borderColour = [\n 'rgb(255, 99, 132)',\n 'rgb(171, 199, 232)',\n 'rgb(255, 127, 14)',\n 'rgb(44, 160, 44)',\n 'rgb(152, 223, 158)',\n 'rgb(214, 39, 40)',\n 'rgb(255, 155, 150)',\n\n ]\n\n return borderColour\n \n },\n\n },\n\n methods: {\n // Expose a method to our <template> and Vue Application\n draw() {\n const ctx = this.$refs.chart\n \n // Render the chart\n const chart = new Chart(ctx, {\n type: 'bar',\n data: {\n labels: [],\n datasets: [{\n label: '',\n axis: 'y',\n data: [],\n backgroundColor: this.backgroundColours,\n\n borderColor: this.borderColors,\n\n borderWidth: 1,\n\n }]\n },\n\n options: {\n indexAxis: 'y',\n plugins: {\n legend: {\n display: this.isLegendDisplay,\n\n },\n \n }, \n \n elements: {\n bar: {\n borderWidth: 1,\n }\n },\n\n parsing: {\n xAxisKey: this.yAxisKey,\n yAxisKey: this.xAxisKey\n\n },\n\n maintainAspectRatio: false, // Let the chart take the shape of the container\n animation: false, // Do not animate\n responsive: true, // - and please be responisve\n \n scales: {\n y: {\n title: {display: this.yAxisTitle ==='' ? false : true,\n text: this.yAxisTitle,\n align: 'center',\n },\n\n ticks: {\n autoSkip: this.isAutoSkip, // Turn off automatic tick count calculations\n },\n stacked: this.isStacked, // Is this a 'stacked' bar chart\n },\n\n x: {\n title: {display: this.xAxisTitle ==='' ? false : true,\n text: this.xAxisTitle,\n align: 'center',\n },\n beginAtZero: true,\n min: this.xAxisMin,\n max: this.xAxisMax,\n stacked: this.isStacked,\n }\n }\n }\n })\n\n // make this available to all elements of the component\n this.chart = chart\n },\n\n onInput(inputData) {\n const chartData = inputData.payload\n const chartTopic = (inputData?.topic) ?? ''\n \n if (Array.isArray(chartData)) {\n if (chartData.length === 0) {\n this.clearChart()\n\n return\n \n } else {\n\n // Configure the dataSets property as the chart datasets and clear any data values\n this.dataSets = this.chart.data.datasets\n this.dataSets.forEach(element => element.data = [])\n\n // For each item in array add the data to proper dataset\n // Series key defaults to ''\n // the data has format [{seriesKey: <string>, xAxisKey: <string>, yAxisKey: <number>}, and many more of such ...]\n chartData.forEach(point => {\n let dataSetName = (this.seriesKey === 'topic') ? chartTopic : ''\n let x = ''\n let y = 0\n if (this.seriesKey === '' || this.seriesKey === 'topic') {\n ({ [this.xAxisKey]: x, [this.yAxisKey]: y } = point)\n\n } else {\n ({ [this.seriesKey]: dataSetName, [this.xAxisKey]: x, [this.yAxisKey]: y } = point)\n\n }\n\n // If an HBar chart the incoming x & y axis data is swapped on the chart\n if (this.chartType === 'HBar') {\n this.addDataPoint(dataSetName, {[this.xAxisKey]: y, [this.yAxisKey]: x})\n\n } else {\n this.addDataPoint(dataSetName, {[this.xAxisKey]: x, [this.yAxisKey]: y})\n\n }\n\n })\n\n this.updateChart()\n \n }\n \n } else {\n this.send({error: `H Bar Chart requires an Array of Objects`})\n }\n\n },\n\n addDataPoint(dataSetName, data) {\n let dataSet = this.dataSets.find(chartSet => chartSet.label === dataSetName)\n if (!dataSet) {\n dataSet = addDataset(dataSetName)\n \n this.dataSets.push(dataSet)\n }\n\n dataSet.data.push(data)\n\n },\n\n updateChart() {\n if (this.chart) {\n this.chart.data.datasets = this.dataSets\n\n this.chart.update()\n }\n\n },\n\n clearChart() {\n if (this.dataSets.length > 0) {\n this.dataSets.forEach(element => element.data = [])\n\n this.updateChart()\n\n }\n\n },\n\n // Add a copy of the first dataset\n addDataset(dataSetName) {\n let dataSet = {...this.chart.data.datasets[0]}\n\n dataSet.label = dataSetName\n\n return dataSet\n \n },\n\n },\n \n mounted() {\n // Code here when the component is first loaded\n let interval = setInterval(() => {\n if (window.Chart) {\n // The chart.js is loaded, so we can now use it\n clearInterval(interval)\n \n this.draw()\n\n this.isChartLoaded = true\n \n }\n\n }, 100)\n\n },\n\n unmounted() {\n // Code here when the component is removed from the Dashboard\n // i.e. when the user navigates away from the page\n }\n \n }\n</script>\n\n<style scoped>\n .chart-container {\n height: 425px;\n position: relative;\n font-size: 1.4rem\n\n }\n\n .chart-title{\n text-align:center;\n\n }\n\n .centre-text {\n text-align: center;\n font-size: 0.8rem; \n }\n\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1320,"y":1000,"wires":[[]],"info":".base-container{\r\n width:100%;\r\n height: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n\r\n}\r\n.chart-container{\r\n position: relative;\r\n margin: auto;\r\n height: 100cqb; /*use container query units to give full height of the container (100% of container height) */\r\n width: 100cqi; /*use container query units to give full width of the container (100% of container height)*/\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}"},{"id":"84c4a8c53f4017b3","type":"ui-template","z":"fe7296cf2a5f7fba","group":"2a9d414817265ee2","page":"","ui":"","name":"Daily Development","order":2,"width":"2","height":"1","head":"","format":"<template>\n <div class=\"base-container\">\n <div class = \"chart-title\">{{chartTitle}}</div>\n <div class = \"chart-container\"><canvas ref=\"chart\"/></div>\n </div> \n\n</template>\n\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n isChartLoaded: false,\n\n chartType: 'HBar',\n isStacked: false,\n \n chartTitle: 'Daily Energy Useage',\n\n // Note that these are the reverse of a normal Bar chart\n yAxisKey: 'period', // String value - label, 'y' is the default\n xAxisKey: 'value', // Number value - data, 'x' is the default\n seriesKey: '', // '' is the default (none)\n\n xAxisMin: 0,\n xAxisMax: 5.0,\n\n yAxisTitle: '',\n xAxisTitle: '',\n\n isLegendDisplay: false,\n isAutoSkip: false,\n\n dataSets: [],\n \n }\n },\n\n watch: {\n msg: function () {\n\n if ( Object.hasOwn(this.msg, 'ui_update') ) {\n \n this.chartTitle = this.msg.ui_update?.chartTitle ?? this.chartTitle\n \n this.yAxisKey = this.msg.ui_update?.yAxisKey ?? this.yAxisKey\n this.xAxisKey = this.msg.ui_update?.xAxisKey ?? this.xAxisKey\n\n this.yAxisMax = this.msg.ui_update?.yAxisMax ?? this.yAxisMax\n this.xAxisMax = this.msg.ui_update?.xAxisMax ?? this.xAxisMax\n\n this.yAxisKey = this.msg.ui_update?.yAxisKey ?? this.yAxisKey\n this.xAxisKey = this.msg.ui_update?.xAxisKey ?? this.xAxisKey\n\n this.yAxisTitle = this.msg.ui_update?.yAxisTitle ?? this.yAxisTitle\n this.xAxisTitle = this.msg.ui_update?.xAxisTitle ?? this.xAxisTitle\n\n } \n\n if (this.isChartLoaded) {\n if (this.msg.payload !== undefined) this.onInput(this.msg)\n\n }\n \n }\n },\n \n computed: {\n backgroundColours: function() {\n let backgroundColour = [\n 'rgba(255, 99, 132, 1.0)',\n 'rgba(171, 199, 232, 1.0)',\n 'rgba(255, 127, 14, 1.0)',\n 'rgba(44, 160, 44, 1.0)',\n 'rgba(152, 223, 158, 1.0)',\n 'rgba(214, 39, 40, 1.0)',\n 'rgba(255, 155, 150, 1.0)',\n ]\n\n return backgroundColour\n \n },\n \n borderColours: function() {\n let borderColour = [\n 'rgb(255, 99, 132)',\n 'rgb(171, 199, 232)',\n 'rgb(255, 127, 14)',\n 'rgb(44, 160, 44)',\n 'rgb(152, 223, 158)',\n 'rgb(214, 39, 40)',\n 'rgb(255, 155, 150)',\n\n ]\n\n return borderColour\n \n },\n\n },\n\n methods: {\n // Expose a method to our <template> and Vue Application\n draw() {\n const ctx = this.$refs.chart\n \n // Render the chart\n const chart = new Chart(ctx, {\n type: 'bar',\n data: {\n labels: [],\n datasets: [{\n label: '',\n axis: 'y',\n data: [],\n backgroundColor: this.backgroundColours,\n\n borderColor: this.borderColors,\n\n borderWidth: 1,\n\n }]\n },\n\n options: {\n indexAxis: 'y',\n plugins: {\n legend: {\n display: this.isLegendDisplay,\n\n },\n \n }, \n \n elements: {\n bar: {\n borderWidth: 1,\n }\n },\n\n parsing: {\n xAxisKey: this.yAxisKey,\n yAxisKey: this.xAxisKey\n\n },\n\n maintainAspectRatio: false, // Let the chart take the shape of the container\n animation: false, // Do not animate\n responsive: true, // - and please be responisve\n \n scales: {\n y: {\n title: {display: this.yAxisTitle ==='' ? false : true,\n text: this.yAxisTitle,\n align: 'center',\n },\n\n ticks: {\n autoSkip: this.isAutoSkip, // Turn off automatic tick count calculations\n },\n stacked: this.isStacked, // Is this a 'stacked' bar chart\n },\n\n x: {\n title: {display: this.xAxisTitle ==='' ? false : true,\n text: this.xAxisTitle,\n align: 'center',\n },\n beginAtZero: true,\n min: this.xAxisMin,\n max: this.xAxisMax,\n stacked: this.isStacked,\n }\n }\n }\n })\n\n // make this available to all elements of the component\n this.chart = chart\n },\n\n onInput(inputData) {\n const chartData = inputData.payload\n const chartTopic = (inputData?.topic) ?? ''\n \n if (Array.isArray(chartData)) {\n if (chartData.length === 0) {\n this.clearChart()\n\n return\n \n } else {\n\n // Configure the dataSets property as the chart datasets and clear any data values\n this.dataSets = this.chart.data.datasets\n this.dataSets.forEach(element => element.data = [])\n\n // For each item in array add the data to proper dataset\n // Series key defaults to ''\n // the data has format [{seriesKey: <string>, xAxisKey: <string>, yAxisKey: <number>}, and many more of such ...]\n chartData.forEach(point => {\n let dataSetName = (this.seriesKey === 'topic') ? chartTopic : ''\n let x = ''\n let y = 0\n if (this.seriesKey === '' || this.seriesKey === 'topic') {\n ({ [this.xAxisKey]: x, [this.yAxisKey]: y } = point)\n\n } else {\n ({ [this.seriesKey]: dataSetName, [this.xAxisKey]: x, [this.yAxisKey]: y } = point)\n\n }\n\n // If an HBar chart the incoming x & y axis data is swapped on the chart\n if (this.chartType === 'HBar') {\n this.addDataPoint(dataSetName, {[this.xAxisKey]: y, [this.yAxisKey]: x})\n\n } else {\n this.addDataPoint(dataSetName, {[this.xAxisKey]: x, [this.yAxisKey]: y})\n\n }\n\n })\n\n this.updateChart()\n \n }\n \n } else {\n this.send({error: `H Bar Chart requires an Array of Objects`})\n }\n\n },\n\n addDataPoint(dataSetName, data) {\n let dataSet = this.dataSets.find(chartSet => chartSet.label === dataSetName)\n if (!dataSet) {\n dataSet = addDataset(dataSetName)\n \n this.dataSets.push(dataSet)\n }\n\n dataSet.data.push(data)\n\n },\n\n updateChart() {\n if (this.chart) {\n this.chart.data.datasets = this.dataSets\n\n this.chart.update()\n }\n\n },\n\n clearChart() {\n if (this.dataSets.length > 0) {\n this.dataSets.forEach(element => element.data = [])\n\n this.updateChart()\n\n }\n\n },\n\n // Add a copy of the first dataset\n addDataset(dataSetName) {\n let dataSet = {...this.chart.data.datasets[0]}\n\n dataSet.label = dataSetName\n\n return dataSet\n \n },\n\n },\n \n mounted() {\n // Code here when the component is first loaded\n let interval = setInterval(() => {\n if (window.Chart) {\n // The chart.js is loaded, so we can now use it\n clearInterval(interval)\n \n this.draw()\n\n this.isChartLoaded = true\n \n }\n\n }, 100)\n\n },\n\n unmounted() {\n // Code here when the component is removed from the Dashboard\n // i.e. when the user navigates away from the page\n }\n \n }\n</script>\n\n<style scoped>\n.base-container{\n width:100%;\n height:100%;\n display: flex;\n justify-content: center;\n align-items: center;\n /*container: chat / size;/*make this container available for container querys*/\n}\n.chart-container{\n position: relative;\n margin: auto;\n height: 100cqb;/*use container query units to give full height of the container (100% of container height) */\n width: 100cqi;/*use container query units to give full width of the container (100% of container height)*/\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1310,"y":1060,"wires":[[]],"info":".base-container{\r\n width:100%;\r\n height: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n\r\n}\r\n.chart-container{\r\n position: relative;\r\n margin: auto;\r\n height: 100cqb; /*use container query units to give full height of the container (100% of container height) */\r\n width: 100cqi; /*use container query units to give full width of the container (100% of container height)*/\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}"},{"id":"2a9d414817265ee2","type":"ui-group","name":"HBar Chart","page":"f14e4c53d983b36e","width":"4","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"f14e4c53d983b36e","type":"ui-page","name":"Scope Example","ui":"80f2e5f9dbf80780","path":"/page10","icon":"home","layout":"grid","theme":"a5bbf4397c8aa75a","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":1,"className":"","visible":true,"disabled":false},{"id":"80f2e5f9dbf80780","type":"ui-base","name":"Environment","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"default","titleBarStyle":"default"},{"id":"a5bbf4397c8aa75a","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#ffffff","groupBg":"#eeeeee","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"6px","groupBorderRadius":"5px","widgetGap":"12px","density":"default"}}]
This is the link I looked at but there are others Vue Scoped Styling
The problem can be circumvented by having different names for the classes, but that rather defeats the object pf the exercise