Uibuilder uibuilder.iife.js errors

Hi,

I have some errors with the new uibuilder..
Just a word of warning, I sense this is gonna be a long thread.

To get started:
These are my current specs:

27 Apr 07:02:08 - [info] Node-RED version: v3.0.2
27 Apr 07:02:08 - [info] Node.js version: v18.16.0
27 Apr 07:02:08 - [info] Windows_NT 10.0.19045 x64 LE
27 Apr 07:02:28 - [info] | uibuilder v6.4.0 initialised

uib packages:

|apexcharts|3.35.0|../uibuilder/vendor/apexcharts/
|bootstrap|5.1.3|../uibuilder/vendor/bootstrap/
|bootstrap-vue|2.21.2|../uibuilder/vendor/bootstrap-vue/
|http-vue-loader|1.4.2|../uibuilder/vendor/http-vue-loader/
|jquery|3.6.0|../uibuilder/vendor/jquery/
|normalize.css|8.0.1|../uibuilder/vendor/normalize.css/
|vega|5.22.1|../uibuilder/vendor/vega/
|vega-embed|6.20.8|../uibuilder/vendor/vega-embed/
|vue|2.6.14|../uibuilder/vendor/vue/
|vue-apexcharts|1.6.2|../uibuilder/vendor/vue-apexcharts/
|vue-router|3.5.3|../uibuilder/vendor/vue-router/

As I've gotten pretty far with my dashboard, I'm not feeling like redo it it some other platform/library/package or anything...

So, VueJS2, vue-router and httpVueLoader

Right, so, as I started this project last year, then abandoned it to do something else, It was only logical to continue when I stopped. But software moved on in this 1 year.

Now, to make things short:

  1. I am using updated software with last-year code (except for the index.html)
  2. When I use it like that (1) there are numerous errors on the console, and redoind routing 3-6 times at one press of a button
  3. When I get back the old index.html, everything works as intended
  4. I tried using new index.js file with "toasts", but as there is no example how to do it without errors and with using the proper vue-routing to jump from vue component to vue component, I gave up and figured this is waaay complicated I hoped it will be

"old" index.html which works:

<!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>FREYA 0.4.2alpha</title>
        <link rel="icon" href="./resources/viking.png">
    <!--<link rel="icon" href="./images/node-blue.ico">-->
        <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
        <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
        <link type="text/css" rel="stylesheet" href="./index.css" media="all">
    </head>

    <body>
        <div id="app" class="selector" v-cloak><!-- All UI code needs to be in here -->
            <Navbar></Navbar>
            <div class="sbar">
                <router-view></router-view>
            </div>

        </div>

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <script src="../uibuilder/vendor/socket.io/socket.io.js">/* REQUIRED: Socket.IO is loaded only once for all instances. Without this, you don't get a websocket connection */</script>
    <!-- Vendor Libraries - Load in the right order, use minified, production versions for speed -->

    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js">/* prod version with component compiler */</script> -->
    <script src="../uibuilder/vendor/vue/dist/vue.js">/* dev version with component compiler */</script>
    <!-- <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js">/* prod version */</script> -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js">/* dev version */</script> 
    <!-- 
    <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
    -->
    <script src="../uibuilder/vendor/vega/build/vega.js"></script>
    <script src="../uibuilder/vendor/vega-embed/build/vega-embed.js"></script>
    <script src="../uibuilder/vendor/apexcharts/dist/apexcharts.js"></script>
    <script src="../uibuilder/vendor/vue-apexcharts/dist/vue-apexcharts.js"></script>
    <!-- 
    <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-apexcharts"></script>
    -->
    <script src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js"></script>
    <script src="../uibuilder/vendor/vue-router/dist/vue-router.js"></script>

    <script src="./uibuilderfe.min.js">/* REQUIRED: remove 'min.' to use dev version */</script>
    <script src="./index.js">/* OPTIONAL: Put your custom code here */</script>
    <!-- #endregion -->
    </body>
</html>

"New" index.html which produces unwanted behaviour:

<!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>FREYA 0.4.2alpha</title>
        <link rel="icon" href="./resources/viking.png">
    <!--<link rel="icon" href="./images/node-blue.ico">-->
        <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
        <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
        <link type="text/css" rel="stylesheet" href="./index.css" media="all">
    </head>

    <body>
        <div id="app" class="selector" v-cloak><!-- All UI code needs to be in here -->
            <Navbar></Navbar>
            <div class="sbar">
                <router-view></router-view>
            </div>

        </div>

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <!-- Vendor Libraries - Load in the right order, use minified, production versions for speed -->

    <script defer src="../uibuilder/vendor/vue/dist/vue.js">/* dev version with component compiler */</script>
    <!-- <script defer src="../uibuilder/vendor/vue/dist/vue.min.js">/* prod version with component compiler */</script> -->  

    <script defer src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js">/* dev version */</script> 
    <!-- <script defer src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js">/* remove 'min.' to use dev version */</script> -->

    <!-- 
    <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
    -->
    <script defer src="../uibuilder/vendor/vega/build/vega.js"></script>
    <script defer src="../uibuilder/vendor/vega-embed/build/vega-embed.js"></script>
    <script defer src="../uibuilder/vendor/apexcharts/dist/apexcharts.js"></script>
    <script defer src="../uibuilder/vendor/vue-apexcharts/dist/vue-apexcharts.js"></script>
    <!-- 
    <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-apexcharts"></script>
    -->
    <script defer src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js"></script>
    <script defer src="../uibuilder/vendor/vue-router/dist/vue-router.js"></script>

    <script defer src="../uibuilder/uibuilder.iife.js"></script>
    <!-- <script defer src="../uibuilder/uibuilder.iife.min.js">/* remove 'min.' to use dev version */</script> -->
    <script defer src="./index.js">/* OPTIONAL: Put your custom code here */</script>
    <!-- #endregion -->
    </body>
</html>

you see, the difference is in the socket.io.js and uibuilderfe.js (uibuilder.iife.js)

So, the errors which I dont have with the old index.html:
image
here: msgToSend._socketId = this._socket.id;

clientWidth missing an ID for the referencing

multiple side refresh and a single router-link action, like this:


I appologize in advance If any of this has been on the forum before - I coudn't find it

For the end, why bother with the "new" uibuilder?

Let me take that first as it is the easier one to answer :slight_smile:

The new uibuilder client is a very extensive rebuild using my greater knowledge and more modern tools and coding practices. So it is a lot less error prone and generally (your experience not withstanding) a lot more robust. It also includes the Socket.IO client library which eliminates a whole set of issues people used to fall foul of.

Most importantly, the new client eliminates a lot of unnecessary boilerplate front-end code and many people won't need to use any front-end code when working with the new uib-element and uib-update nodes, fewer people will find themselves being forced to use a framework like Vue as well. So for a lot of people, the new client and the new nodes make using uibuilder a breeze. In addition, that approach allows people to stay close to vanilla HTML which means that things learned while using uibuilder are easily transferable to actual front-end coding should uibuilder not quite do what they want.

When used with Vue v2, it is true that there are fewer benefits other than some simplification in the front-end code and the removal of bugs.

Need to come back to the other issues as I need to present to my new team now. Back later.

I just found the reason most of the errors were happening.

As I inserted my old index.html from the last year, and wanted to test it a bit further, I saw a message in the console: "uibuilder already started. Do not start it more than once".

Then I went to look out for the uibuilder.start and saw I was using it in every component.
You see, as I don't know the rules (as I dont have a programmind background), I had no idea I shouldnt use the same JS data in every vue component.

I think the new IIFE library should have reported it.

Sorry to bother you with this, I know you have lots on your mind. Can you insted give me some link to documentation which would explain the functioning of "modern" uibuilder vith vuejs2, routing and components?

Ah, well that was another of the things that the new client made redundant. And for good reason as you have discovered. It's just that it wasn't easy logic to get right and I could not find a reliable approach for a long time. Indeed, it took the major reorganisation of the client library to enable me to work out the logic.

Well, it kind of did didn't it? It did say that you shouldn't do it more than once.

However, you've reminded me to add something to the development backlog. Up till now, most errors in the client are only reported to the client. Which means that if you aren't looking at the browser console, you wont see them. For those people with little front-end development experience, that is easily missed. So I've been working up some designs to get errors and other log messages back into Node-RED as well. I will add this as an example scenario to what kind of logging needs to be surfaced in Node-RED.

Hey, not a problem.

Just got to go pick my wife up, I'll come back to you later.

The best thing to start with for basic Vue v2 is from the examples.
image

Try the templates example which shows examples for each of the various templates. The two you will be interested in are:

image

As for routing. I don't have an example using the uibuilder v6 "new" client I'm afraid, only the old example from AFelix in the WIKI: Using Vue Router · TotallyInformation/node-red-contrib-uibuilder Wiki (github.com). Let me see if I can update that.

Really, using Vue Router isn't a uibuilder thing since once you've got uibuilder working with Vue, the only thing left is to listen for messages from Node-RED using the uibuilder.onChange function and assigning any data to an appropriate Vue data variable or running a Vue method. As I don't use Vue Router, there is a limit to how much I can help. However, if you end up with a nice working example, please do add it to the WIKI then let me know so I can add it to the menu.

1 Like

First problem with vue-router is one of the other reasons I've given up on Vue - the stupidity of messing up a major version change on a platform where you have thousands of down-stream packages!

The documentation for the vue-router package doesn't even explain how to install the blasted thing :frowning:

So if you want to use it you have to "know" that if using Vue v2, you need vue-router v3! To install that in uibuilder, you add the library vue-router@3 on the library manager tab.

Grrrrrr.

I didn't see it with uibuilder.iife.js.
This message came out with uibuilderfe.js and socket.io.js.



I'm kinda lost to what all this means. Toasts etc.... More important, I dont know how to handle multiple vue components with vue-router in this "new" uibuilder..



Sure, I will do that. ATM i cleared my dashboard of all unecesary code and it work just fine, especialy after removing those pesky uibuilder.starts :slight_smile:
But still I dont quite understand whats needed where - I will have to watch some vueJS2 tutorials.



I know, I just want to finish this project ASAP (which is taking long time because of various CSS, SVG and ocasionally VueJS problems)

Ah, it is probably a warning not an error (since you CAN run it more than once and it should not error). Warnings are now suppressed unless you turn them on with uibuilder.logLevel = 1 (or = 'warn).

Bear with me, I'm getting you there :slight_smile:

The life of a front-end developer is never easy I'm afraid, even using Dashboard throws up loads of issues once you get past the basics.


Anyway, enough waffle from me, here is a VERY basic example using router.

Start by adding a new uibuilder node so that you don't mess up the thing you are building, give it a suitable name and click deploy to create the basics. Then re-open the new node settings and change the template to "Simple VueJS v2" and click on the load button then accept the warning.

You already have the correct libraries installed so go to the File tab and replace the following files with the code below:

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>VueJS + bootstrap-vue simple template - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - VueJS + bootstrap-vue simple template">

    <link rel="icon" href="../uibuilder/images/node-blue.ico">

    <!-- #region Supporting CSS. -->
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <link type="text/css" rel="stylesheet" href="./index.css" media="all"><!-- Your own CSS -->
    <!-- #endregion -->

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / - socket.io no longer needed -->
    <script defer src="../uibuilder/vendor/vue/dist/vue.min.js">/* prod version with component compiler */</script>
    <script defer src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js">/* remove 'min.' to use dev version */</script>
    <script defer src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js"></script>
    <script defer src="../uibuilder/vendor/vue-router/dist/vue-router.js"></script>
    <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
    <script defer src="./index.js">/* OPTIONAL: Put your custom code in that */</script>
    <!-- #endregion -->
    
</head><body>

    <div id="app" class="uib" v-cloak><!-- All UI code needs to be in here -->
        <b-container id="app_container"><!-- Wraps the bootstrap-vue formatting -->
            <h1 class="with-subtitle">uibuilder + Vue v2 + bootstrap-vue - Simple Template</h1>
            <div role="doc-subtitle">Using the uibuilder IIFE library.</div>

            <div>
                <!-- use router-link component for navigation. -->
                <!-- specify the link by passing the `to` prop. -->
                <!-- `<router-link>` will be rendered as an `<a>` tag by default -->
                <router-link to="/foo">Go to Foo</router-link> | 
                <router-link to="/bar">Go to Bar</router-link>
            </div>

            <div id="more"><!-- '#more' is used as a parent for dynamic content in examples --></div>

            <!-- route outlet - components matched by the route will render here -->
            <router-view></router-view>
        </b-container>
    </div>

</body></html>

index.js

/* eslint-disable object-shorthand */
// @ts-nocheck

/** Example of using the IIFE build of the uibuilder client library with VueJS v2
 * Note that uibuilder.start() should no longer be needed.
 * See the docs if the client doesn't start on its own.
 */
'use strict'

// 0. If using a module system (e.g. via vue-cli), import Vue and VueRouter
// and then call `Vue.use(VueRouter)`.

// 1. Define route components.
// These can be imported from other files
const Foo = { template: '<div><b-card>This is the FOO content defined in index.js</b-card></div>' }
const Bar = { template: '<div><b-card>This is the BAR content defined in index.js</b-card></div>' }

// 2. Define some routes
// Each route should map to a component. The "component" can
// either be an actual component constructor created via
// `Vue.extend()`, or just a component options object.
const routes = [
    { path: '/foo', component: Foo },
    { path: '/bar', component: Bar }
]

// 3. Create the router instance and pass the `routes` option
const router = new VueRouter({
    routes // short for `routes: routes`
})

// eslint-disable-next-line no-unused-vars
const app = new Vue({
    el: '#app',

    data() {
        return {
            // Add reactive data variables here
        }
    }, // --- End of data --- //

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {

        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        uibuilder.onChange('msg', (msg) => {
            this.lastMsg = msg

            // Workaround to show "toast" notifications.
            if (msg._uib && msg._uib.componentRef && msg._uib.componentRef === 'globalNotification') {
                this.$bvToast.toast(msg._uib.options.content, msg._uib.options)
                console.log(msg)
            }
        })

    }, // --- End of created hook --- //

    // Make sure to inject the router with the router option to make the
    // whole app router-aware.
    router,

}) // --- End of app definition --- //

// EOF

Nearly all of that is actually the template content but it is easier just to copy/paste everything.

Now your test page should like like this:

Of course, that is only "loading" two routes that are defined in JavaScript which, while fine for simple things isn't so helpful for more complex stuff.

You'll need to bear with me for a more extended example as I need to break for dinner now. :slight_smile: But have a go with the above and make sure you can get it working. I'll do an extended example with http-vue-loader later.


PS: One more thing. If you check out the vue-router v3 getting started page, you will see that the code I've added to the simple vue v2 template comes from their starter example.

1 Like

Here we go, just a couple of minor amendments lets us load a route from a .vue file thanks to http-vue-loader:

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>VueJS + bootstrap-vue simple template - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - VueJS + bootstrap-vue simple template">

    <link rel="icon" href="../uibuilder/images/node-blue.ico">

    <!-- #region Supporting CSS. -->
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <link type="text/css" rel="stylesheet" href="./index.css" media="all"><!-- Your own CSS -->
    <!-- #endregion -->

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / - socket.io no longer needed -->
    <script defer src="../uibuilder/vendor/vue/dist/vue.min.js">/* prod version with component compiler */</script>
    <script defer src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js">/* remove 'min.' to use dev version */</script>
    <script defer src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js"></script>
    <script defer src="../uibuilder/vendor/vue-router/dist/vue-router.js"></script>
    <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
    <script defer src="./index.js">/* OPTIONAL: Put your custom code in that */</script>
    <!-- #endregion -->
    
</head><body>

    <div id="app" class="uib" v-cloak><!-- All UI code needs to be in here -->
        <b-container id="app_container"><!-- Wraps the bootstrap-vue formatting -->
            <h1 class="with-subtitle">uibuilder + Vue v2 + bootstrap-vue - Simple Template</h1>
            <div role="doc-subtitle">Using the uibuilder IIFE library.</div>

            <div>
                <!-- use router-link component for navigation. -->
                <!-- specify the link by passing the `to` prop. -->
                <!-- `<router-link>` will be rendered as an `<a>` tag by default -->
                <router-link to="/foo">Go to Foo</router-link> | 
                <router-link to="/bar">Go to Bar</router-link> | 
                <router-link to="/over">Go Over</router-link>
            </div>

            <div id="more"><!-- '#more' is used as a parent for dynamic content in examples --></div>

            <!-- route outlet - components matched by the route will render here -->
            <router-view></router-view>
        </b-container>
    </div>

</body></html>

index.js

/* eslint-disable object-shorthand */
// @ts-nocheck

/** Example of using the IIFE build of the uibuilder client library with VueJS v2
 * Note that uibuilder.start() should no longer be needed.
 * See the docs if the client doesn't start on its own.
 */
'use strict'

// 0. If using a module system (e.g. via vue-cli), import Vue and VueRouter
// and then call `Vue.use(VueRouter)`.

// 1. Define route components.
// These can be imported from other files
const Foo = { template: '<div><b-card>This is the FOO content defined in index.js</b-card></div>' }
const Bar = { template: '<div><b-card>This is the BAR content defined in index.js</b-card></div>' }

// 1a. Lets load a .vue component file
const OverView = httpVueLoader('./OverView.vue')

// 2. Define some routes
// Each route should map to a component. The "component" can
// either be an actual component constructor created via
// `Vue.extend()`, or just a component options object.
const routes = [
    { path: '/foo', component: Foo },
    { path: '/bar', component: Bar },
    { path: '/over', component: OverView }
]

// 3. Create the router instance and pass the `routes` option
const router = new VueRouter({
    routes // short for `routes: routes`
})

// eslint-disable-next-line no-unused-vars
const app = new Vue({
    el: '#app',

    data() {
        return {
            // Add reactive data variables here
        }
    }, // --- End of data --- //

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {

        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        uibuilder.onChange('msg', (msg) => {
            this.lastMsg = msg

            // Workaround to show "toast" notifications.
            if (msg._uib && msg._uib.componentRef && msg._uib.componentRef === 'globalNotification') {
                this.$bvToast.toast(msg._uib.options.content, msg._uib.options)
                console.log(msg)
            }
        })

    }, // --- End of created hook --- //

    // Make sure to inject the router with the router option to make the
    // whole app router-aware.
    router,

}) // --- End of app definition --- //

// EOF

And the .vue file called OverView.vue, put it in the same place as your index files:

<template>
    <div class="mydiv">
        <h2>{{ title }}</h1>
        <p>{{ message }}</p>
    </div>
</template>

<script>
module.exports = {
  name: 'MyComponent',
  data() {
    return {
      title: 'Hello VueJS!',
      message: 'This is a simple VueJS component loaded from a .vue file by http-vue-loader.'
    }
  }
}
</script>

<style>
    /* CSS styles go here */
    .mydiv {
      margin-top: 2em;
      padding: 0.5em;
      border: 2px solid red;
    }
</style>

Gives you this:

1 Like

It looks so simple when you do it :slight_smile:

So, no created, no mounted, no computed in a vue component? I thought they have to be there (for reason unknown to me :D)
Also no toast config and other stuff which is in the main JS file. I was wondering what need to stay in the main JS and what I need to move to components too..

Thank you very much. I will adapt it to my project. Hope there will be no problems, as I see there is zero warnings and errors on yours example

No, you don't NEED them, you can USE them if you need them though.

You mentioned Toast a few times. I guess that's my fault and I should explain. In the old client, I put a couple of functions in that worked natively with VueJS (this was before modern HTML got so useful). One of them used bootstrap-vue's b-toast tag to display a message on-screen. You could send a simple message from Node-RED and it would appear on the page, very handy.

When I did the new client, I cleared all that out since the majority of people will never need or use it.

Instead, I implemented a simpler message overlay that was pure HTML and JavaScript. No need for bootstrap-vue. But then, if you were using bootstrap-vue, your pretty messages were not quite as pretty (though actually I have implemented most of the features).

So the small piece of code in the template that talks about Toasts is a simple way of wiring up and using a node-red message to trigger a bootstrap-vue toast message. Frankly, I probably should have left it off since it is so trivial.

If you are happy with the way that the client displays messages natively, then you don't need to worry about the toast.

That really is up to you. How easy you find it do have everything in a main js file vs the overheads of creating and loading .vue files. There is not absolute answer to be honest. But if you think your page is likely to get a bit complex then it is worth using components.

It isn't that bad once you've got your head around the structure.

1 Like

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