One of the issues that some people find when switching to uibuilder from Dashboard is that they are faced with writing more HTML/CSS/JavaScript front-end code than they may be used to (or comfortable with).
I chose VueJS and bootstrap-vue as the default templates from v2 because I believe that they offer the most capability with the simplest learning curve and minimum "boilerplate" code.
Even so, many things could be simpler still and I've been thinking about and writing about this for some while, trying to get thoughts and designs together.
Well, it seems that inspiration has finally struck. and here is a quick and easy example of where I'm intending to go. Follow the other thread for design discussions but this article is something that anyone can easily add to their own uibuilder app.
It provides a really easy way to create "toast" pop-over notifications on any uibuilder web page that uses VueJS and bootstrap-vue. You simply send the configuration data and config from Node-RED in a standardised format that I'll be utilising for more extension components in the future (as I hope will other people also creating components).
Add the following function to your index.js
file. It goes outside the var app1 = new Vue({...})
part. You don't need to understand how it works, you can simply copy/paste it across. But I'm happy to explain it if anyone wants.
/** Simple function to create a toast notification from an incoming msg
* Requires a reference to a VueJS instance and a msg object from Node-RED.
* Place inside the uibuilder.on('msg', ...) function inside your Vue app's
* mounted section.
* @see https://bootstrap-vue.org/docs/components/toast
* @param {Vue} vueApp A reference to a VueJS app instance
* @param {Object} msg A msg from Node-RED with appropriate formatting
*/
function makeToast(vueApp, msg) {
// Make sure that we have Vue loaded with the $bvToast function
// That lets us dynamically create a toast object directly in the virtual DOM
if ( ! vueApp.$bvToast ) {
console.warn('[uibuilder] bootstrap-vue toast component not available')
return
}
// $createElement is a Vue function that lets you create Vue virtual DOM
// elements. We use it here to let us render HTML in the toast.
const h = vueApp.$createElement
/** Toast options
* @type {Object} toastOptions Optional metadata for the toast.
* @param {String|vNodes|vNodes[]} [toastOptions.title] Optional title, may be HTML (vNode or array of vNodes)
* @param {Boolean} [toastOptions.appendToast] Optional. Whether to show new toasts below previous ones still on-screen (true). Or to replace previous (false - default)
* @param {Number} [toastOptions.autoHideDelay] Optional. Ms until toast is auto-hidden.
* See the bootstrap-vue docs for more options
*/
let toastOptions = {}
/** Main content of the toast
* @type {String|vNodes|vNodes[]}
*/
let content = ''
// Main body content
if ( msg.payload ) content += msg.payload
if ( msg._uib.options.content ) content += msg._uib.options.content
// Assume that the input content is or could be HTML. create a virtual DOM element
const vNodesContent = h(
'p', {
domProps: {
innerHTML: content
}
}
)
if ( msg._uib.options ) toastOptions = Object.assign({}, msg._uib.options) // Need a copy here otherwise debug output breaks
// The title is also allowed to have HTML
if ( msg._uib.options.title ) toastOptions.title = h(
'p', {
domProps: {
innerHTML: msg._uib.options.title
}
}
)
// Do we want new toasts to be shown at the bottom of the list (true) instead of the top (false - default)?
if ( msg._uib.options.append ) toastOptions.appendToast = msg._uib.options.append
// If set, number of ms until toast is auto-hidden
if ( msg._uib.options.autoHideDelay ) toastOptions.autoHideDelay = msg._uib.options.autoHideDelay
// Toast wont show anyway if content is empty, may as well warn user
if ( content === '' ) {
console.warn('[uibuilder] Toast content is blank. Not shown.')
return
}
// Dynamically insert the toast to the virtual DOM
// Will show at top-right of the HTML element that is the app root
// unless you include a <b-toaster> element
vueApp.$bvToast.toast(vNodesContent, toastOptions)
} // --- End of makeToast() --- //
Then in your uibuilder.onChange('msg', function(msg){ ... })
function, add this:
// Testing a notifications scheme - to be moved into uibuilderfe
if (msg._uib && msg._uib.componentRef && msg._uib.componentRef === 'globalNotification') {
makeToast(vueApp, msg)
}
Now try sending some messages to uibuilder using the following format:
{
"payload": "",
"_uib": {
"componentRef": "globalNotification",
"options": {
"title": "This is the <i>title</i>", /* & $string($floor($random()*10)), */
"content": "This is content <span style=\"color:red;\">in addition to</span> the payload",
"append": true,
"autoHideDelay": 1500,
"variant": "info",
"solid": true,
"href": "https://bbc.co.uk",
"toaster": "b-toaster-top-center",
"noAutoHide": true
}
}
}
All of the options are optional. You can find the details at:
So there you have it. A very simple but comprehensive way to show user notifications.
I will, at some point most likely be including that within the uibuilderfe code so that you don't need to write any code at all! Just as easy as Dashboard and hopefully a lot more performant since you are only sending minimal data and not any code.
Check out the other thread where I also talk about new extension components that provide a lot more capability. I'm using a graphical gauge component as an example to work through the ideas. That will be really simple to use as well and should nicely demonstrate combining the power and flexibility of uibuilder with the simplicity found in Dashboard.
Let me know what you think.