-
-
Notifications
You must be signed in to change notification settings - Fork 86
Simple Caching Example
A simple example of how to do caching by topic with a function node is included with uibuilder v2.0.2 or above. You can import the example from the library in Node-RED.
Alternatively, you can use the following flow which contains all the code necessary:
[
{
"id": "ae4ad803.98aa88",
"type": "uibuilder",
"z": "18cb249f.38bafb",
"name": "",
"topic": "",
"url": "cachetest1",
"fwdInMessages": false,
"allowScripts": true,
"allowStyles": true,
"copyIndex": true,
"showfolder": false,
"x": 530,
"y": 1300,
"wires": [
[
"47aeb5cb.97f31c"
],
[
"a330534f.2e678",
"4c2899fb.1cd318"
]
]
},
{
"id": "47aeb5cb.97f31c",
"type": "debug",
"z": "18cb249f.38bafb",
"name": "data from ui",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"x": 710,
"y": 1300,
"wires": []
},
{
"id": "43f62f54.e076c",
"type": "function",
"z": "18cb249f.38bafb",
"name": "cache by topic",
"func": "/* jshint asi: true */\n\n// Expects input msgs with topic set\n\n// cache variable name - needs to be unique for each cache\nconst cacheVarName = 'msgCache'\n\n// get saved context\nlet msgCache = context.get(cacheVarName) || {}\n\n// Handle cache control messages\nif (msg.hasOwnProperty('cacheControl')) {\n // Replay cache if requested\n if (msg.cacheControl === 'REPLAY') {\n for (let topic in msgCache) {\n // Only send to a single client if we can\n if ( msg.hasOwnProperty('_socketId') ) {\n node.send({\n \"topic\": topic, \n \"payload\": msgCache[topic],\n \"_socketId\": msg._socketId\n })\n } else {\n node.send({\n \"topic\": topic, \n \"payload\": msgCache[topic]\n })\n }\n }\n return null\n } else if (msg.cacheControl === 'CLEAR') {\n // Or clear the cach (no msg sent)\n context.set(cacheVarName, {})\n return null\n } else {\n // or do nothing\n return null\n }\n}\n\n\n// ignore other uibuilder control messages\nif ( msg.hasOwnProperty('uibuilderCtrl') ) return null\n\n// Keep the last msg.payload by topic\nmsgCache[msg.topic] = msg.payload\n\n// save context for next time\ncontext.set(cacheVarName, msgCache)\n\n// Show number of cached msgs in status\nnode.status({fill:'green',shape:'ring',text:'Cached: ' + Object.keys(msgCache).length})\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 330,
"y": 1300,
"wires": [
[
"ae4ad803.98aa88"
]
]
},
{
"id": "a330534f.2e678",
"type": "debug",
"z": "18cb249f.38bafb",
"name": "control msgs from ui",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"x": 740,
"y": 1340,
"wires": []
},
{
"id": "122cefe6.c39a7",
"type": "link in",
"z": "18cb249f.38bafb",
"name": "home-replay",
"links": [
"4c2899fb.1cd318",
"8c23b9bc.b992b8"
],
"x": 215,
"y": 1300,
"wires": [
[
"43f62f54.e076c"
]
]
},
{
"id": "4c2899fb.1cd318",
"type": "link out",
"z": "18cb249f.38bafb",
"name": "home-controls",
"links": [
"122cefe6.c39a7"
],
"x": 655,
"y": 1380,
"wires": []
},
{
"id": "a22357f8.abfa88",
"type": "comment",
"z": "18cb249f.38bafb",
"name": "Caching using simple function by msg.topic",
"info": "See https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Cache-and-Replay-Messages-without-using-node-red-contrib-infocache\nfor details.\n\nThe uibuilder control messages are used to \nreplay or empty the cache.\n\nFeed in your own data to the cache to see how it\nworks.\n\nThe way it is currently written, the last message\nof each topic passing through the function node\nis retained and then replayed as separate messages\nwhen a new client GETs the uibuilder URL (or when\nand existing client reloads the page).\n\n## Configuration\n\nReplace default `index.html` and `index.js` files\nwith those in the 2 comment nodes.\n\nChange the name of the cache variable if copying this function.",
"x": 700,
"y": 1260,
"wires": []
},
{
"id": "7ec2ab22.136164",
"type": "inject",
"z": "18cb249f.38bafb",
"name": "Input Trigger",
"topic": "RANDOM-TEST",
"payload": "",
"payloadType": "date",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 155,
"y": 1260,
"wires": [
[
"9d09f684.33be68"
]
],
"outputLabels": [
"Trigger"
],
"l": false
},
{
"id": "9d09f684.33be68",
"type": "change",
"z": "18cb249f.38bafb",
"name": "Random #",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "$random()*10",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 215,
"y": 1260,
"wires": [
[
"43f62f54.e076c"
]
],
"inputLabels": [
"Trigger"
],
"outputLabels": [
"Random Number"
],
"l": false
},
{
"id": "6dc4ac8d.557764",
"type": "comment",
"z": "18cb249f.38bafb",
"name": "index.html",
"info": "<!doctype html>\n<html lang=\"en\">\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes\">\n\n <title>Cache Test : uibuilder</title>\n <meta name=\"description\" content=\"Node-RED UI Builder Cache Test\">\n\n <link rel=\"icon\" href=\"./images/node-blue.ico\">\n\n <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css\" />\n <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css\" />\n\n <link rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n</head><body>\n <div id=\"app\">\n <b-container id=\"app_container\">\n <h1>\n UIbuilder Cache Test Example\n </h1>\n <p>\n This is a uibuilder test using a simple function as a cache.\n Trigger the input a couple of times then load a new window or tab.\n The value below should almost instantly go from '[Nothing]' to\n that last sent value. However, the number of received messages should\n drop back down to 2.\n </p>\n <h2>Data from Node-RED</h2>\n <b-card>\n Random Number: {{nrRandom}}\n </b-card>\n\n <p>\n Messages: Received={{msgsReceived}}\n </p>\n\n </b-container>\n </div>\n\n <!-- These MUST be in the right order. Note no leading / -->\n <!-- REQUIRED: Socket.IO is loaded only once for all instances\n Without this, you don't get a websocket connection -->\n <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n\n <!-- --- Vendor Libraries - Load in the right order --- -->\n <script src=\"../uibuilder/vendor/vue/dist/vue.js\"></script> <!-- dev version with component compiler -->\n <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.min.js\"></script> prod version with component compiler -->\n <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.runtime.min.js\"></script> prod version without component compiler -->\n <!-- <script src=\"https://unpkg.com/babel-polyfill@latest/dist/polyfill.min.js\"></script> for older browsers -->\n <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js\"></script>\n\n <!-- REQUIRED: Sets up Socket listeners and the msg object -->\n <!-- <script src=\"./uibuilderfe.js\"></script> //dev version -->\n <script src=\"./uibuilderfe.min.js\"></script> <!-- //prod version -->\n <!-- OPTIONAL: You probably want this. Put your custom code here -->\n <script src=\"./index.js\"></script>\n\n</body></html>\n",
"x": 320,
"y": 1260,
"wires": [],
"icon": "node-red/parser-html.svg"
},
{
"id": "243438af.12e268",
"type": "comment",
"z": "18cb249f.38bafb",
"name": "index.js",
"info": "/* jshint browser: true, esversion: 5, asi: true */\n/* globals document,Vue,window,uibuilder */\n// @ts-nocheck\n/*\n Copyright (c) 2019 Julian Knight (Totally Information)\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\n/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */\n\nconst app1 = new Vue({\n el: '#app',\n data: {\n nrRandom : '[Nothing]',\n msgsReceived: 0,\n msgsControl : 0,\n msgsSent : 0,\n msgRecvd : '[Nothing]',\n msgSent : '[Nothing]',\n msgCtrl : '[Nothing]',\n }, // --- End of data --- //\n computed: {\n }, // --- End of computed --- //\n methods: {\n }, // --- End of methods --- //\n\n // Available hooks: init,mounted,updated,destroyed\n mounted: function(){\n\n /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3\n * Pass the namespace and ioPath variables if hosting page is not in the instance root folder\n * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.\n * e.g. uibuilder.start('/nr/uib', '/nr/uibuilder/vendor/socket.io') // change to use your paths/names\n */\n uibuilder.start()\n\n var vueApp = this\n\n // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO\n // Note that you can also listen for 'msgsReceived' as they are updated at the same time\n // but newVal relates to the attribute being listened to.\n uibuilder.onChange('msg', function(msg){\n console.info('[uibuilder.onChange] property msg changed!', msg)\n vueApp.msgRecvd = msg\n vueApp.msgsReceived++\n\n vueApp.nrRandom = msg.payload\n\n })\n\n } // --- End of mounted hook --- //\n\n}) // --- End of app1 --- //\n\n// EOF\n",
"x": 450,
"y": 1260,
"wires": [],
"icon": "font-awesome/fa-code"
},
{
"id": "75c87fd8.f149a",
"type": "inject",
"z": "18cb249f.38bafb",
"name": "",
"topic": "",
"payload": "",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 215,
"y": 1380,
"wires": [
[
"27c93453.13df8c"
]
],
"l": false
},
{
"id": "27c93453.13df8c",
"type": "change",
"z": "18cb249f.38bafb",
"name": "Clear Cache",
"rules": [
{
"t": "set",
"p": "cacheControl",
"pt": "msg",
"to": "CLEAR",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 310,
"y": 1380,
"wires": [
[
"8c23b9bc.b992b8"
]
]
},
{
"id": "8c23b9bc.b992b8",
"type": "link out",
"z": "18cb249f.38bafb",
"name": "",
"links": [
"122cefe6.c39a7"
],
"x": 415,
"y": 1380,
"wires": []
}
]
Please feel free to add comments to the page (clearly mark with your initials & please add a commit msg so we know what has changed). You can contact me in the Discourse forum, or raise an issue here in GitHub! I will make sure all comments & suggestions are represented here.
-
Walkthrough 🔗 Getting started
-
In Progress and To Do 🔗 What's coming up for uibuilder?
-
Awesome uibuilder Examples, tutorials, templates and references.
-
How To
- How to send data when a client connects or reloads the page
- Send messages to a specific client
- Cache & Replay Messages
- Cache without a helper node
- Use webpack to optimise front-end libraries and code
- How to contribute & coding standards
- How to use NGINX as a proxy for Node-RED
- How to manage packages manually
- How to upload a file from the browser to Node-RED
-
Vanilla HTML/JavaScript examples
-
VueJS general hints, tips and examples
- Load Vue (v2 or v3) components without a build step (modern browsers only)
- How to use webpack with VueJS (or other frameworks)
- Awesome VueJS - Tips, info & libraries for working with Vue
- Components that work
-
VueJS v3 hints, tips and examples
-
VueJS v2 hints, tips and examples
- Dynamically load .vue files without a build step (Vue v2)
- Really Simple Example (Quote of the Day)
- Example charts using Chartkick, Chart.js, Google
- Example Gauge using vue-svg-gauge
- Example charts using ApexCharts
- Example chart using Vue-ECharts
- Example: debug messages using uibuilder & Vue
- Example: knob/gauge widget for uibuilder & Vue
- Example: Embedded video player using VideoJS
- Simple Button Acknowledgement Example Thanks to ringmybell
- Using Vue-Router without a build step Thanks to AFelix
- Vue Canvas Knob Component Thanks to Klaus Zerbe
-
Examples for other frameworks (check version before trying)
- Basic jQuery example - Updated for uibuilder v6.1
- ReactJS with no build - updated for uibuilder v5/6
-
Examples for other frameworks (may not work, out-of-date)
-
Outdated Pages (Historic only)
- v1 Examples (these need updating to uibuilder v2/v3/v4/v5)