Flows from "Writing Custom Templates - FlowFuse Dashboard" Tutorial
These flows are those built when following the "Writing Custom Templates - FlowFuse Dashboard" YouTube tutorial, published by FlowFuse.
It guides you on how to build your own widgets for FlowFuse Dashboard (also known as Node-RED Dashboard 2.0).
Dependencies
The following, additional, packages will need to be installed via the Node-RED Palette Manager, or npm, in order for the flows to run.
[{"id":"51a8e2eaa201f97a","type":"inject","z":"391328aec32f274c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":80,"wires":[["bb2d08de5dfd6486"]]},{"id":"bb2d08de5dfd6486","type":"http request","z":"391328aec32f274c","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://api.open-notify.org/iss-now.json","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":330,"y":100,"wires":[["f15545c51c86ff28","99e772cb2d69f37a"]]},{"id":"136e4b6bee7e277c","type":"worldmap","z":"391328aec32f274c","name":"","lat":"","lon":"","zoom":"","layer":"","cluster":"","maxage":"","usermenu":"show","layers":"show","panit":"false","panlock":"false","zoomlock":"false","hiderightclick":"false","coords":"false","showgrid":"false","showruler":"false","allowFileDrop":"false","path":"/worldmap","overlist":"DR,CO,RA,DN","maplist":"OSMG,OSMC,EsriC,EsriS,UKOS","mapname":"","mapurl":"","mapopt":"","mapwms":false,"x":760,"y":60,"wires":[]},{"id":"f15545c51c86ff28","type":"debug","z":"391328aec32f274c","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":100,"wires":[]},{"id":"99e772cb2d69f37a","type":"function","z":"391328aec32f274c","name":"function 1","func":"msg = {\n payload: {\n name: \"ISS\",\n lat: msg.payload.iss_position.latitude,\n lon: msg.payload.iss_position.longitude,\n icon: \"iss\"\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":60,"wires":[["136e4b6bee7e277c","05c44b3a70226a37","2ac7883786d1dac0","076fbd504c3a834f"]]},{"id":"3cff02cbe32e88fb","type":"ui-template","z":"391328aec32f274c","group":"fde680bc13eac73c","page":"","ui":"","name":"Raw Web Tech","order":1,"width":0,"height":0,"head":"","format":"<template>\n <h1 id=\"my-header\">Hello World</h1>\n</template>\n\n<script>\ndocument.getElementById(\"my-header\").innerHTML = \"Updated Header\"\n</script>\n\n<style>\n h1 {\n color: red;\n }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":780,"y":160,"wires":[[]]},{"id":"05c44b3a70226a37","type":"ui-template","z":"391328aec32f274c","group":"36e086084db4d144","page":"","ui":"","name":"In Built Stuff","order":1,"width":0,"height":0,"head":"","format":"<template>\n <div>\n {{ msg.payload }}\n </div>\n <button onclick=\"onClick()\">Click Me!</button>\n</template>\n\n<script>\n window.onClick = () => {\n this.send({\n payload: 'Button Clicked'\n })\n }\n</script>\n\n<style>\nbutton {\n background-color: black;\n color: white;\n padding: 6px;\n}\n</style>\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":770,"y":120,"wires":[["d60878d3a283ab52"]]},{"id":"d60878d3a283ab52","type":"debug","z":"391328aec32f274c","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":920,"y":120,"wires":[]},{"id":"a3661c376af7f109","type":"ui-template","z":"391328aec32f274c","group":"e6ef4e2f832b5560","page":"","ui":"","name":"iframe","order":1,"width":"6","height":"8","head":"","format":"<template>\n <iframe src=\"/worldmap\"></iframe>\n</template>\n\n<style>\niframe {\n width: 100%;\n height: 100%;\n}\n</style>\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":750,"y":200,"wires":[[]]},{"id":"02e27045e9f73272","type":"ui-button","z":"391328aec32f274c","group":"ac686ff4425de150","name":"","label":"Update Location","order":1,"width":0,"height":0,"emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","iconPosition":"left","payload":"","payloadType":"date","topic":"topic","topicType":"msg","buttonColor":"","textColor":"","iconColor":"","enableClick":true,"enablePointerdown":false,"pointerdownPayload":"","pointerdownPayloadType":"str","enablePointerup":false,"pointerupPayload":"","pointerupPayloadType":"str","x":130,"y":120,"wires":[["bb2d08de5dfd6486"]]},{"id":"2ac7883786d1dac0","type":"ui-template","z":"391328aec32f274c","group":"ac686ff4425de150","page":"","ui":"","name":"","order":2,"width":0,"height":0,"head":"","format":"<template>\n <div class=\"location\" :class=\"hemisphere.toLowerCase()\">\n Location: {{ msg.payload }}\n </div>\n <div>\n <label>Hemisphere:</label>\n <span>{{ hemisphere }}</span>\n </div>\n <!-- Conditional Logic -->\n <label v-if=\"hemisphere === 'Southern'\">You're in the South!</label>\n <label v-else>You're not in the South!</label>\n <!-- List Rendering -->\n <ul>\n <li v-for=\"location in locations\">{{ location }}</li>\n </ul>\n</template>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n locations: [\n 'London',\n 'Paris',\n 'Tokyo'\n ]\n }\n },\n computed: {\n hemisphere: function () {\n if (this.msg?.payload?.lat > 0) {\n return \"Northern\"\n } else if (this.msg?.payload?.lat < 0) {\n return \"Southern\"\n } else {\n return \"None\"\n }\n }\n },\n methods: { },\n mounted() {\n // code here when the component is first loaded\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<style>\n /* define any styles here - supports raw CSS */\n .location {\n font-weight: bold;\n }\n .location.southern {\n color: blue;\n }\n .location.northern {\n color: red;\n }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":760,"y":20,"wires":[[]]},{"id":"effbd7be410b0cab","type":"ui-template","z":"391328aec32f274c","group":"6958c0d43e4af796","page":"","ui":"","name":"Form","order":1,"width":0,"height":0,"head":"","format":"<template>\n <v-form @submit.prevent=\"calculate\">\n <div>\n <v-text-field v-model=\"lat\" label=\"Latitude\"></v-text-field>\n </div>\n <div>\n <v-text-field v-model=\"lon\" label=\"Longitude\"></v-text-field>\n </div>\n <v-btn type=\"submit\" block>Send</v-btn>\n </form>\n</template>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n lat: 0,\n lon: 0\n }\n },\n methods: {\n calculate: function () {\n this.send({\n payload: {\n name: 'Your Location',\n lat: this.lat,\n lon: this.lon\n }\n })\n }\n }\n }\n</script>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":330,"y":260,"wires":[["136e4b6bee7e277c","19fd32a9462d7848","0e921f9a56c10863"]]},{"id":"19fd32a9462d7848","type":"debug","z":"391328aec32f274c","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":260,"wires":[]},{"id":"076fbd504c3a834f","type":"ui-template","z":"391328aec32f274c","group":"b4294af8315736a3","page":"","ui":"","name":"","order":1,"width":0,"height":0,"head":"","format":"<template>\n <v-card v-for=\"location in locations\">\n <v-img :src=\"location.img\" class=\"align-end\" gradient=\"to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)\" height=\"200px\" cover>\n <v-card-title class=\"text-white\" v-text=\"location.name\"></v-card-title>\n </v-img>\n \n <v-card-actions>\n <v-spacer></v-spacer>\n {{ calculateDistance(location.coords.lat, location.coords.lon) }} km\n </v-card-actions>\n </v-card>\n <v-card >\n <v-img :src=\"\" class=\"align-end\" gradient=\"to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)\" height=\"200px\"\n cover>\n <v-card-title class=\"text-white\">Your Location</v-card-title>\n </v-img>\n \n <v-card-actions>\n <v-spacer></v-spacer>\n {{ calculateDistance(yourLocation.lat, yourLocation.lon) }} km\n </v-card-actions>\n </v-card>\n</template>\n\n<script>\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n locations: [{\n name: 'London',\n img:\n 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Palace_of_Westminster_from_the_dome_on_Methodist_Central_Hall_%28cropped%29.jpg/900px-Palace_of_Westminster_from_the_dome_on_Methodist_Central_Hall_%28cropped%29.jpg',\n coords: {\n lat: 51.32,\n lon: 0.5\n }\n }, {\n name: 'Paris',\n img:\n 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Seine_and_Eiffel_Tower_from_Tour_Saint_Jacques_2013-08.JPG/1200px-Seine_and_Eiffel_Tower_from_Tour_Saint_Jacques_2013-08.JPG',\n coords: {\n lat: 48.48,\n lon: 2.2\n }\n }, {\n name: 'Tokyo',\n img: 'https://upload.wikimedia.org/wikipedia/commons/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg',\n coords: {\n lat: 35.4,\n lon: 139.45\n }\n }],\n iss: {\n lat: 0,\n lon: 0\n },\n yourLocation: {\n lat: 0,\n lon: 0\n }\n }\n },\n computed: {\n\n },\n methods: {\n // expose a method to our <template> and Vue Application\n calculateDistance: function (cityLat, cityLon) {\n const iss = this.iss\n var distance = 'Unknown'\n if (iss) {\n const issLat = this.deg2rad(iss.lat)\n const issLon = this.deg2rad(iss.lon)\n \n cityLat = this.deg2rad(cityLat)\n cityLon = this.deg2rad(cityLon)\n \n const partA = Math.sin(issLat) * Math.sin(cityLat)\n const partB = Math.cos(issLat) * Math.cos(cityLat) * Math.cos(cityLon - issLon)\n distance = 6371 * Math.acos(partA + partB)\n \n // round distance to 2 dp\n distance = Math.round(distance * 100) / 100\n }\n return distance\n },\n deg2rad: function (deg) {\n return deg * (Math.PI / 180)\n }\n },\n mounted () {\n this.$socket.on('msg-input:' + this.id, (msg) => {\n if (msg.form) {\n // store form data\n this.yourLocation = msg.form\n }\n if (msg.payload) {\n // store the ISS data\n this.iss = msg.payload\n }\n })\n }\n }\n</script>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":720,"y":300,"wires":[[]]},{"id":"0e921f9a56c10863","type":"change","z":"391328aec32f274c","name":"","rules":[{"t":"set","p":"form","pt":"msg","to":"payload","tot":"msg"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":300,"wires":[["076fbd504c3a834f"]]},{"id":"fde680bc13eac73c","type":"ui-group","name":"Starting Example","page":"9c6bbb686c561118","width":"6","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"36e086084db4d144","type":"ui-group","name":"In Built Stuff","page":"9c6bbb686c561118","width":"6","height":"1","order":2,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"e6ef4e2f832b5560","type":"ui-group","name":"IFRAME","page":"9c6bbb686c561118","width":"6","height":"1","order":3,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"ac686ff4425de150","type":"ui-group","name":"ISS Tracker","page":"9c6bbb686c561118","width":"6","height":"1","order":4,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"6958c0d43e4af796","type":"ui-group","name":"Your Location","page":"9c6bbb686c561118","width":"6","height":"1","order":5,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"b4294af8315736a3","type":"ui-group","name":"Locations","page":"9c6bbb686c561118","width":"6","height":"1","order":6,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"9c6bbb686c561118","type":"ui-page","name":"Page 1","ui":"53046a510b1f819d","path":"/page1","icon":"home","layout":"notebook","theme":"bafff2709d88aaef","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":"53046a510b1f819d","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"default","titleBarStyle":"default"},{"id":"bafff2709d88aaef","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094CE","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]