Uibuilder import data from local js file and export to it

Hello,

I want to make several (js) files in uibuilder to be imported when needed, and also to serve as a storage.

As I have no idea how to import and use a js file, I've tried this:

in src/views/overview.vue
in data: test: {},
in created:

const test = require('../resources/test.js');
this.test = {test};

console.log('testis: ', this.test.testis);
console.log('test objekt: ', (JSON.stringify(this.test)));

test.js has the following content:

const data = {
    test: {
      testis: 5
    }
  };
  
module.exports = data;

the data items is always empty, no matter I get it in created or beforeCreate, or I get console.log in monted

have you defined your main index.js file as type module ?

...
    <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
    <script defer type="module" src="./index.js"></script>
...

test.js can be

export const data = {
    test: {
      testis: 5
    }
  };

and in the .vue file you can use import { data } from "../resources/test.js"

and try to console.log(data) in Vue lifecycle hook created

ps. how are you loading those .vue files .. are you using some bandler like vite ?

Remember the difference between what happens in Node-RED (a node.js server app that supports require and import) and the front-end (your index.html, index.js, etc where require is not supported at all and import is only supported in HMTL ES Modules so you have to use the ESM version of the client library).

As Andy helpfully points out, you simply create another .js file in the same folder as index.js and then load it using a script tag as he has shown the index.js file being loaded.

However, to us a .vue file directly, you must either "build" them into a .js file or load them dynamcially using something like the http-vue-loader library. Examples in the uibuilder WIKI.

Home · TotallyInformation/node-red-contrib-uibuilder Wiki (github.com)

No, I cant load the vue files with httpVueLoader then

I'm getting errors if I do that:
image

I'm using httpVueLoader

Try FranckFreiburger/vue3-sfc-loader: Single File Component loader for Vue2 and Vue3. Load .vue files directly from your HTML. No node.js environment, no build step. (github.com) instead. That works with Vue 2 & 3.

1 Like

yes .. i remember having some issue too when i first tried out httpVueLoader
either use Julian's link for the vue3-sfc-loader
or use a bundler like Vite

I have a sample template Github repo of a simple navigation bar using Vue2, bootstrap-vue and vue-router if you want to browse through the code and test. it uses vite to build the app
uib-template-vue-navbar

2 Likes

Ok, ty. I will try.

This is actualy my first true use of uibuilder, thats why i'm having all sorts of questions. It will be pretty comprehensive too, with loads of dynamic css moving png around and over the bottom layered svgs. Charts and tables will be inside svgs as you click them. Over svg also need to be gauge charts. I'm not sure how will it all behave after its been completed, so maybe I'll have to build it after all

Why it's everything so hard?

I've been trying to get it running for 2 hors now.

errors from the first example (the page doesnt even load):

image



Errors from the second example (only 1 and a half component loaded):


image

This is the code for declaring components:

const Navbar = Vue.component('Navbar', () => loadModule('./components/Navbar.vue', options));
const Overview = Vue.component('Overview', () => loadModule('./views/Overview.vue', options));
....
const routeDefs = [
    {
        path: '/',
        component: Overview
    },{
        path: '/glueline',
        component: GlueLine
    },{
        path: '/Settings',
        component: Settings
    }
]

const app = new Vue({

    el: '#app',
    router: new VueRouter({routes: routeDefs}),
    components: {
        Navbar,

I just dont understand....

P.S. everything worked OK with httpVueLoader, but I cant import JS and export to it

Do you have your full code on Github so we can test this ?

One of the reasons I no longer use any frameworks.


Here is some code that should work I think. Though I think that it is using Vue v3 so if you have Vue v2 installed, switch the vue library script line to the commented out version to load from CDN to save you messing about.

index.html

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

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

    <title>Simple Vue3 with new uib client - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - Simple Vue3 with new uib client">
    <link rel="icon" href="./images/node-blue.ico">

    <!-- Your own CSS -->
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / - socket.io no longer needed  -->
    <!-- <script defer src="https://cdn.jsdelivr.net/npm/vue@latest/dist/vue.global.prod.js">/* Remove .prod for development version */</script> -->
    <script defer src="../uibuilder/vendor/vue/dist/vue.global.prod.js">/* Remove .prod for development version */</script>
    <script defer src="../uibuilder/vendor/vue3-sfc-loader/dist/vue3-sfc-loader.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 class="uib">

    <div id="app">
        <h1>Simple Vue v3 App Using New uibuilder IIFE Client - v6.1.0</h1>

        <p>{{ message }}</p>

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

        <div style="border:2px solid silver;">
            <my-component color="yellow"></my-component>
        </div>

        <p>
            <button @click="count++" title="Not sent back to Node-RED - this just shows that VueJS is working">
                Simple VueJS Counter: {{ count }}
            </button>
        </p>
        
        <p>
            <button @click="doEvent" data-mything="This is a thing" title="This is sent back to Node-RED">
                Click to send msg to Node-RED
            </button>
        </p>
        
        <p>
            <label for="input1">This is sent to Node-RED when you exit it: </label>
            <input v-model="input1" @change="doInputChange" id="input1" type="text" placeholder="Type text then exit field">
        </p>
        
        <pre id="msg" v-html="formatLastRcvd" class="syntax-highlight">Waiting for a message from Node-RED</pre>
    </div>

</body></html>

index.js

// @ts-nocheck
'use strict'

const { createApp } = Vue
const { loadModule } = window["vue3-sfc-loader"]

// See vue3-sfc-loader docs for details.
const sfcOptions = {

    moduleCache: {
        vue: Vue,
    },

    getFile(url) {
        return fetch(url).then(response => response.ok ? response.text() : Promise.reject(response));
    },

    addStyle(styleStr) {
        const style = document.createElement('style');
        style.textContent = styleStr;
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
    },

    log(type, ...args) {
        console.log(type, ...args);
    }
}

// Using the Vue options API style for beginner simplicity
// No need to pre-define Quasar's $q when working with the options API
const app = createApp({
    components: {
        'my-component': Vue.defineAsyncComponent(() => loadModule('./myComponent.vue', sfcOptions)),
    },

    // Define Vue reactive variables
    data() {
        return {

            message: 'Hello Vue!',
            count: 0,
            lastMsgRecvd: '[Nothing]',
            input1: '',

        }
    },

    // Dynamic data
    computed: {

        // This is auto-recalculated by Vue when lastMsgRecvd changes
        formatLastRcvd() {
            const lastMsgRecvd = this.lastMsgRecvd
            if (typeof lastMsgRecvd === 'string') return 'Last Message Received = ' + lastMsgRecvd
            return 'Last Message Received = ' + uibuilder.syntaxHighlight(lastMsgRecvd)
        },

    },

    // Supporting functions
    methods: {

        // Use the uib helper function to send something to NR
        doEvent(event) { uibuilder.eventSend(event) },

        /** Runs when the change event for the source field is fired
         * @param {InputEvent} event The event object is passed by the browser automatically
         */
        doInputChange(event) {
            console.log('input1 text has changed: ', event.target.value)
            // Send the new text to Node-RED
            uibuilder.send({
                'topic': 'input1-changed',
                'payload': event.target.value,
            })
        },

    },

    // Lifecycle hooks
    mounted() {
        // If msg changes - msg is updated when a standard msg is received from Node-RED
        uibuilder.onChange('msg', (msg) => {
            console.log('>> msg recvd >>', msg, this)
            this.lastMsgRecvd = msg
        })
    },
})

app.mount('#app')

myComponent.vue

<template>
    <div>
        <p>
            The VueJS component called <a href="https://github.com/FranckFreiburger/vue3-sfc-loader" target="_blank">vue3-sfc-loader</a>
            provides the ability to dynamically load a Vue component from a <code>.vue</code> file without the need for a pre-build step.
        </p>
        <P>
            It is worth noting that this also works with VueJS v2. 
            See the <a href="https://github.com/FranckFreiburger/vue3-sfc-loader/blob/main/docs/examples.md#vue2-basic-example" target="_blank">docs</a> for details.
        </P>
        <p>
            This example uses a dynamically loaded <span class="example">{{ msg }}</span> file!
        </p>
    </div>
</template>

<style scoped>
    .example {
        color: v-bind('color');
    }
    p::before {
        content: "VUE file content: ";
        font-variant-caps: small-caps;
        font-style: italic;
    }
</style>

<script>
    export default {
        data() {
            return {
                msg: 'myComponent.vue',
                color: 'red',
            }
        }
    }
</script>

It's not mine. Yet


Ok, it's working. However, there are some minor warnings:
vue.global.js:1661 [Vue warn]: Template compilation error: v-html will override element children.
27 |          </p>
28 |          
29 |          <pre id="msg" v-html="formatLastRcvd" class="syntax-highlight">Waiting for a message from Node-RED</pre>
   |                        ^^^^^^^^^^^^^^^^^^^^^^^
30 |       
  at <App>


vue.global.js:1661 [Vue warn]: Failed to resolve component: P
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 
  at <Anonymous color="yellow" > 
  at <AsyncComponentWrapper color="yellow" > 
  at <App>

Not sure If I want to go with it though. In my tests I've found comments sometimes throws warnings, also, the imported component css doesn't load local images... Plus, I'ts way slower and not sure if it's suited for a great number of components, as it loaded them much slower (maybe I used the cdn version, not local):
const Navbar = httpVueLoader('./components/Navbar.vue');
const GlueLine = httpVueLoader('./views/GlueLine.vue');
const Overview = httpVueLoader('./views/Overview.vue');
const Settings = httpVueLoader('./views/Settings.vue');
/*const test1 = httpVueLoader('./tests/test1.vue');
const star1 = httpVueLoader('./tests/star1.vue');
const test2 = httpVueLoader('./tests/test2.vue');
const test3 = httpVueLoader('./tests/test3.vue');
const test4 = httpVueLoader('./tests/test4.vue');
const alphaLj1 = httpVueLoader('./tests/alphaLj1.vue');*/
const svgLoader = httpVueLoader('./svgs/svgLoader.vue');
//const g9320 = httpVueLoader('./tests/g9320.vue');
const svgLj1 = httpVueLoader('./svgs/p2_lj1.vue');
const svgLj2 = httpVueLoader('./svgs/p2_lj2.vue');
const svgLj3 = httpVueLoader('./svgs/p2_lj3.vue');
const svgPak1 = httpVueLoader('./svgs/p2_pak1.vue');
const svgPak2 = httpVueLoader('./svgs/p2_pak2.vue');
const svgPak3 = httpVueLoader('./svgs/p2_pak3.vue');
const svgTop = httpVueLoader('./svgs/p2_top.vue');
const svgTrans = httpVueLoader('./svgs/p2_transp.vue');
const svgTric = httpVueLoader('./svgs/p2_tric.vue');

Too much bother to just load some objects from a js file - in the meantime I loaded the data I needed externaly from the Node-RED.

Thx for the effort though

Never said it was perfect :grin:

Not sure what you mean by that. Any images in CSS are the same as in your HTML, they need to come from the server - uibuilder in this case.

Yes, that is to be expected. As you say, it is designed for small apps not big ones. If you are doing a big Vue app, same as any other framework, you should use a build step to "compile" everything. That will always be way more efficient in use.

Yup. I mentioned that I'm no longer using any frameworks. uibuilder on its own with Node-RED does pretty much everything I was doing with frameworks before - just using vanilla HTML/CSS - I'm hardly ever even using front-end JavaScript either now.

If I really needed something complex, I might use Svelte to lend a hand but even there, I would probably just use uib-element instead with some additional tweaks using the low-code config to create some HTML, copy that HTML and save to the index.html file so it wasn't being dynamically created each time and then use uib-update (or the low-code config directly) to update things as needed.

Probably wouldn't scale to enterprise-grade usage with many 100's of users but for a lot of things, it is more than enough.

1 Like