So here was my fun exercise for today - rewarding myself for having done some more uibuilder documentation updates ready for the v6.1.0 release (coming soon - honest!).
It occurred to me that caching data you want to send to your front-end UI is not the only way to do things, especially if you are dynamically creating a UI from Node-RED.
A little research later and I came up with something that is certainly going into the new front-end client library as an option.
Save the whole HTML to browser local storage after it changes!
Yup, that really is doable - HTML really has changed a lot since the early days.
If you want to play along, you don't need to even install a dev version of uibuilder - because you can do it all in your own custom code as shown here.
Start with a new uibuilder node set to use the IIFE template. No changes are needed to the HTML.
For your index.js
file, add the following to the default template:
function clearHtmlCache() {
uibuilder.removeStore('htmlCache')
}
document.addEventListener('uibuilder:socket:connected', (evt) => {
// Select the node that will be observed for mutations
// const targetNode = $('html')
const targetNode = document.getElementsByTagName('html')[0]
// console.log('started', evt)
const htmlCache = uibuilder.getStore('htmlCache')
if (htmlCache) {
// Restore the entire HTML
targetNode.innerHTML = htmlCache
// Blank out the old received msg
const eMsg = document.getElementById('msg')
if (eMsg) eMsg.innerText = 'Waiting for a message from Node-RED'
}
// Create an observer instance linked to the callback function
const observer = new MutationObserver( function() {
// We don't need to know the details - so kill off any outstanding mutation records
this.takeRecords()
// Save the updated entire HTML in localStorage
uibuilder.setStore('htmlCache', targetNode.innerHTML)
} )
// Start observing the target node for configured mutations
observer.observe(targetNode, { attributes: true, childList: true, subtree: true, characterData: true })
})
Now, send something to your uibuilder node that dynamically adds to the UI. Here is an example inject and function node combination that will do the job:
[{"id":"d8165c992562f583","type":"inject","z":"56443195ea782ac2","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":125,"y":100,"wires":[["6249c54d3b768791"]],"l":false},{"id":"6249c54d3b768791","type":"function","z":"56443195ea782ac2","name":"New Card","func":"let cardCounter = context.get('cardCounter') ?? 0\n\nmsg = {\n \"_ui\": [\n {\n \"method\": \"remove\",\n \"components\": [\n \"#mycard\"\n ]\n },\n {\n \"method\": \"add\",\n \"parent\": \"#more\",\n \"components\": [\n {\n \"type\": \"div\",\n \"attributes\": {\n \"id\": \"mycard\",\n \"title\": \"This is my Card\",\n \"style\": \"max-width: 20rem;border:solid silver 1px;margin-bottom:1rem;\",\n },\n \"components\": [\n {\n \"type\": \"h2\",\n \"slot\": \"A New Card\",\n \"attributes\": {\n \"class\": \"complementary\",\n \"style\": \"text-align:center;margin-top:0;\"\n }\n },\n {\n \"type\": \"p\",\n \"slot\": \"Some text in a paragraph.\"\n },\n {\n \"type\": \"p\",\n \"slot\": \"Another paragraph. Count: \" + ++cardCounter\n }\n ]\n }\n ],\n }\n ]\n}\ncontext.set('cardCounter', cardCounter)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":230,"y":100,"wires":[["7d274212fa8b1c65"]],"info":"Inserts a pure HTML \"card\" into a div called `#more`.\r\nIf that div does not exist, will add to the bottom of the HTML.\r\n\r\nFirstly attempts to remove the div so that you only ever have 1.\r\n\r\nAn example of using uibuilder's dynamic UI configuration-driven\r\nbuilding capabilities without the need for any fancy nodes or\r\nframeworks. Pure HTML. But you can still utilise the extra\r\nfeatures of your favourite framework too if you like!"}]
Once you've updated the UI, simply reload the page and hey-presto, the added "card" is still there! No Node-RED caching needed at all.
If you want to reset things, you will need to open your browser console and type in clearHtmlCache()
- or of course you could set up a msg listener to do that when you send a particular message, I'll leave that as an excercise for yourselves though.
I think this is going to be really helpful for future steps into low-/no-code UI creation with uibuilder. As I say, this will get built into the client along with the ability to send the whole HMTL back to Node-RED as-well/instead to give even more options.
Have fun!