Migrate from ui-template to ui-builder

I'm building a dynamic dashboard. The result I want is a grid of cards, like the bootstrap grid system.

I'm currently doing this with a single template node, to which I'm providing a msg.template. I include a link to bootstrap.min.css and use the grid layout with a header image, a few list items, and a footer for each card.

It looks ok, but it's stuck in the bounds of the group that it's nested under, this causes a bunch of bugs, like horizontal scrollbars etc. It's also not responsive.

How do I go from what I have now, to UI-Builder? I installed ui-builder and looked at all the examples. I don't see a big difference between all the types of vue, vue3, svelte etc, and I don't know which would be best.

I just want a URL that shows cards dynamically, responsively, in a grid, while remaining lightweight and stateless. Ideally, it's something that looks pretty good out of the box.

1 Like

Hi Vincent,

You may not need a framework at all other than bootstrap itself if that is what you are used to. But if you use bootstrap, you may wish to comment out the loading of the uib-brand.css load in index.css. And if you do that, keep an eye out for any of the built-in uibuilder displays - most notably the showMsg display that gives an on-screen formatted view of the last message sent by uibuilder to the front-end - you might want to copy over the syntaxHighlight classes to your index.css to get the right view. Other than that, you can use bootstrap as per any of its documentation or articles.

Personally, I'm still getting my head around using CSS grid and flex to achieve what you describe and so the uib-brand.css file does contain some classes to support such a layout. Check out a post I did very recently that produces a status dashboard that looks like this:

That produces HTML that looks like this:

Where you can see that each group of status boxes uses a div that has a grid class and each individual status box has a flex class. You will see all the class definitions in uib-brand.css assuming you have uibuilder v6.1 installed.

This is the flow that produces the box. It takes an input object that defines all of the things to monitor. The actual statuses come from MQTT.

The layout is actually created by a template node (not a ui_template) because it was simply quicker for me to define the structure using Mustache templating. I intend to build this layout into the next uibuilder release as one of the options in the zero-code uib-element node since it seems to be a useful, generic status layout.

Indeed, I intend to expand on the grid/flex layouts in the next release along the lines you have described. But I'm still learning the best way to do that myself :slight_smile:

Indeed! So your first choice is whether you want to spend some time learning about vanilla HTML/CSS grid and flex (there are quite a number of online helpers by the way that help create grid/flex layouts for you) or whether you want to spend some time learning a framework.

If you choose the framework route, VueJS v2 with bootstrap-vue is likely to be far and away the quickest to get up and running with simply because it has bootstrap built in along with a bunch of helper components that will let you quickly produce grid/flex style layouts so your HTML coding will be minimal. Again, plenty of online tutorials to help and the bootstrap-vue documentation is pretty good.

Hope this helps? Let me know what you think. Happy to share the flow BTW but it will likely need some tweaking depending on how you report statuses to MQTT.

thank you so much for your detailed answer!

what benefits are gained by using vue2 vs vue3? is vueJS v2 different than vue2? and what is bootstrap-vue, this is another layer that runs on top of vue?

I'm reading from a database to populate the data, not via mqtt.

currently I'm just using ${msg.foo.bar} variables to insert them into the html that is sent to the template node. How is this process different in uibuilder?

No, vue@2 is what you put into uibuilder's library manager to ensure that you install the latest v2 release. VueJS is the official name of the framework, vue2 is just shorthand.

Well, possibly a bit of a personal viewpoint but I think that:

  • It is simpler and easier to learn v2, certainly less confusing since v3 has several ways of doing things in order to try and carry forwards some legacy compatibility.
  • There are way more tutorials and extended components for v2 than there are for v3 which is still relatively new (and can sometimes be quite complex to migrate).
  • On the other hand, v2 is at end of life and the future is v3.

The biggest gain right now in my view for v2 is that you can use bootstrap-vue which is quite possibly the easiest extension to Vue that I've tried - though admittedly my knowledge on that might be slightly dated.

If you want an extension for Vue v3, possible Quasar is the easiest to get going with. However, I still didn't find that personally as easy as bootstrap-vue.

Bootstrap-vue are preparing for Vue v3 compatibility but I couldn't get it to work in my last test. That was a fairly brief test though since I no longer use Vue myself. Most of what I do doesn't really need Vue or bootstrap and is easily met by uibuilder with vanilla HTML/CSS with an occasional bit of JS thrown in for good measure. But my needs are pretty simple, mostly some fairly simple home automation dashboards like the one I showed previously. This is possibly because I spend all my free time working on uibuilder! :grin:

MQTT has the advantage that you get an event-driven data interface, effectively for free. With a DB, you have to go and query it when you want an update. But yes, either will work just fine.

If you are using something like Vue, then the difference is how you get the node-red data into the UI. With the "traditional" way of working with uibuilder, you send your msg direct to the uibuilder node and in your front-end code, you have a uibuilder.onchange('msg', (msg) => { ... }) event listener. That function fires whenever the client receives a message from Node-RED. You then assign the data from the incoming msg to a Vue data variable. These are defined in your View "app" and they are reactive to change. So the act of updating the variable will change the UI. That method still works as expected and won't be going away, it continues to be useful.

Without using a framework, you can now use the uib-update node and you don't need any front-end javascript at all. So if your HTML contained:

<div>
    <h2>My Interesting Web Page</h2>
    <p>Some fixed text.</p>
    <p id="changableP">(this will be changed from node-red)</p>
</div>

You can use a uib-update node like this:

And every time you send a msg via this node to the uibuilder node with a payload set to a string, that string will replace the on-page text with the new text.

Note the leading # on the CSS Selector field in the node and how it relates to the id attribute that you set on the p tag where you want the text.

You can, of course, use <span> tags in your html instead. A p tag is a "block" whereas a span is "inline". The point is that uibuilder doesn't care, it leverages the standard features of HTML and CSS but makes it easier for you to use - you don't have to learn loads about HTML and CSS, just the basics and you'd be learning at least that much in order to learn how to use a framework.

the update trick definitely looks helpful, thank you.

the challenge is that I don't know how many cards I will need in advance, it depends on what my function finds in the database query. Hence, additional rows need to be added accordingly. Currently I do that in the function that generates the html for the template node.

How do I handle such a thing with UI builder?

In the same way :grin:

Feed your data into a template node (not a ui_template) and use Mustache to translate.

This does the same thing:

Here is the mustache template to give you an idea of how it works.

<main>
    {{#flow[file].bookmarks}}
    <div class="centre status-grid">
        <h2 class="status-heading">{{group}}</h2>
        {{#checks}}
        {{#link}}<a href="{{link}}" target="_blank" class="status-link">{{/link}}
        <div id="st-{{id}}" class="box flex surface4" title="MQTT Topic: {{statusTopic}}">
            
            <div class="status-side-panel"></div>
            <div>
                <div>{{name}}</div>
                <div class="text-smaller">{{description}}</div>
            </div>
        </div>
        {{#link}}</a>{{/link}}
        {{/checks}}
    </div>
    {{/flow[file].bookmarks}}
</main>

Once you have the HTML, you can send it to a uib-element node set to HTML. Then send that to uibuilder. In the example show, I send to a uib-cache node first so and connect the output of the uibuilder control output port back to the cache so that newly connected clients (or reloads) are automatically sent the cache and so get the last output from your DB without you having to query it again.

PS: If you can share an example output from your DB query, I can probably help you with the Mustache.

ahh I see. Ok, I'm getting a bit of clarity.

What happens here:

When I select one of the frameworks (like the one highlighted), is that all I need to do? Or do I also need to then go to libraries and add the correct ones?

If you select a template, uibuilder gives you a warning message of any missing dependencies.
and once you load a template, an additional message that any code you have under that uibuilder folder will get replaced.

image

so yes .. you have to install them manually in the Libraries tab.
in this case vue@2 and bootstrap-vue
(along with any other npm packages that you may need for your project)

1 Like

And that gives you just a template. So a place to start from. Some basic hints.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.