UIBuilder Table Implementation 2.0

Hy all,
in the past I made it possible to implement a few great things in NodeRed. Thanks to the forum and a few very kind people here.
Nevertheless I got stuck again with the following example:
Example from 2021

Due that the post is from 2021 and closed I asumed it is the best option to open a new topic.

Anyways, what I am trying to do is the same as in the post:

  • fetch data payload from ms sql database
[{"name":"D-5909","type":"Arcus","manufacturer":"Schempp-Hirth Flugzeugbau"},{"name":"OE-9513","type":"DG-1001M","manufacturer":"DG Flugzeugbau"},{"name":"D-5809","type":"Duo Discus","manufacturer":"Schempp-Hirth Flugzeugbau"},{"name":"OE-5663","type":"FOX","manufacturer":"MDM-1"},{"name":"OE-5280","type":"ASK 21","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-5664","type":"LS8","manufacturer":"Rolladen-Schneider"},{"name":"D-6521","type":"LS1","manufacturer":"Rolladen-Schneider"},{"name":"D-3578","type":"LS4","manufacturer":"Rolladen-Schneider"},{"name":"OE-5338","type":"LS4","manufacturer":"Rolladen-Schneider"},{"name":"OE-5563","type":"LS4","manufacturer":"Rolladen-Schneider"},{"name":"OE-5211","type":"ASK 13","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"D-3987","type":"ASK 13","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"D-8525","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-5287","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-5295","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-0772","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"D-KNAM","type":"SF-25C Falke","manufacturer":"Scheibe"},{"name":"OE-ARD","type":"Husky","manufacturer":"Aviat Aircraft"}]
  • rap it in a bootstrap table using uibuilder

I am just wondering if the example from post back from 2021 is not working anymore (due to changes) or I cant see the woods in front of hundred trees ^^

Is somebody getting the example to work?
Flow:

[{"id":"4c55571eab755af4","type":"uibuilder","z":"affcf244.5a4cd","name":"","topic":"","url":"table","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":432000,"tokenAutoExtend":false,"oldUrl":"uibuilder","reload":false,"credentials":{},"x":460,"y":560,"wires":[["eeafe87409b1a331"],[]]},{"id":"33f5b55cb3bf51a0","type":"inject","z":"affcf244.5a4cd","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"ROWID\":11,\"EXACTTIME\":\"Friday_June11th2021_5:22:42pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 5:22 pm\",\"FIRSTNAME\":\"Wells\",\"LASTNAME\":\"Fargo\"},{\"ROWID\":10,\"EXACTTIME\":\"Friday_June11th2021_5:17:46pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 5:17 pm\",\"FIRSTNAME\":\"advance\",\"LASTNAME\":\"auto\"},{\"ROWID\":9,\"EXACTTIME\":\"Friday_June11th2021_4:55:48pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 4:55 pm\",\"FIRSTNAME\":\"test\",\"LASTNAME\":\"user\"},{\"ROWID\":8,\"EXACTTIME\":\"Friday_June11th2021_4:55:32pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 4:55 pm\",\"FIRSTNAME\":\"advance\",\"LASTNAME\":\"auto\"},{\"ROWID\":7,\"EXACTTIME\":\"Friday_June11th2021_4:55:23pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 4:55 pm\",\"FIRSTNAME\":\"Wells\",\"LASTNAME\":\"Fargo\"},{\"ROWID\":6,\"EXACTTIME\":\"Friday_June11th2021_4:48:24pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 4:48 pm\",\"FIRSTNAME\":\"Wells\",\"LASTNAME\":\"Fargo\"},{\"ROWID\":5,\"EXACTTIME\":\"Friday_June11th2021_4:48:15pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 4:48 pm\",\"FIRSTNAME\":\"advance\",\"LASTNAME\":\"auto\"},{\"ROWID\":4,\"EXACTTIME\":\"Friday_June11th2021_4:48:05pm\",\"TIMESTAMP\":\"Friday, June 11th 2021, 4:48 pm\",\"FIRSTNAME\":\"test\",\"LASTNAME\":\"user\"}]","payloadType":"json","x":200,"y":560,"wires":[["4c55571eab755af4"]]},{"id":"eeafe87409b1a331","type":"debug","z":"affcf244.5a4cd","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":640,"y":560,"wires":[]}]

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <title>Node-RED UI Builder - VueJS + bootstrap-vue default template</title>
    <meta name="description" content="Node-RED UI Builder - VueJS + bootstrap-vue default template" />

    <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" />
    <!-- Your own CSS -->
    <link type="text/css" rel="stylesheet" href="./index.css" media="all" />
  </head>
  <body>
    <div id="app" v-cloak>
      <b-container id="app_container" class="mt-5">
        <h1>UIbuilder + Vue.js + bootstrap-vue table for Node-RED</h1>
        <div class="mt-5">
          <b-table striped hover small :items="items" sort-by="ROWID"></b-table>
        </div>
      </b-container>
    </div>

    <!-- These MUST be in the right order. Note no leading / -->

    <!-- REQUIRED: Socket.IO is loaded only once for all instances. Without this, you don't get a websocket connection -->
    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>

    <!-- Vendor Libraries - Load in the right order, use minified, production versions for speed -->
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script>
    <!-- dev version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
    <!-- Dev version -->
    <!-- <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js"></script>   Prod version -->

    <!-- REQUIRED: Sets up Socket listeners and the msg object -->
    <script src="./uibuilderfe.js"></script>
    <!-- dev version -->
    <!-- <script src="./uibuilderfe.min.js"></script>     prod version -->

    <!-- OPTIONAL: You probably want this. Put your custom code here -->
    <script src="./index.js"></script>
  </body>
</html>

index.js

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
/*
  Copyright (c) 2021 Julian Knight (Totally Information)

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/
"use strict";

/** @see https://totallyinformation.github.io/node-red-contrib-uibuilder/#/front-end-library */

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

  data() {
    return {
         items: []  // table data 
        
        };
  }, // --- End of data --- //

  computed: {}, // --- End of computed --- //

  methods: {}, // --- End of methods --- //

  // Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured

  /** Called after the Vue app has been created. A good place to put startup code */
  created: function() {
    // Example of retrieving data from uibuilder
    this.feVersion = uibuilder.get("version");

    uibuilder.start();
  }, // --- End of created hook --- //

  /** Called once all Vue component instances have been loaded and the virtual DOM built */
  mounted: function() {
    //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')

    var app = this; // Reference to `this` in case we need it for more complex functions

    // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
    uibuilder.onChange("msg", function(msg) {
      console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
      app.items = msg.payload;  // save new table data from node-red in vue data items
      
    });

   
  }, // --- End of mounted hook --- //
}); // --- End of app1 --- //

// EOF

THX for guiding

Things have moved on a bit since then so there are a few lines that you need to adjust.

For example, if you use the new client library, you don't need to load socket.io at all (it is done for you). But you need to change from ./uibuilderfe.js to ../uibuilder/uibuilder.iife.min.js.

You also need to make sure that you have the correct version of VueJS installed. Since they moved from v2 to v3, if you want to use the older examples, it is best to make sure you have Vue v2 installed.

The best thing to do is to make sure you have the latest version of uibuilder and then use one of the current examples. And make sure that, when using uibuilder to install Vue via the library manager, specify vue@2 as the library to install. Once you have an example running, you can start to add your own Vue code.

1 Like

@TotallyInformation thx for the hint.

I followed your advice to set up the examples from the template folders and moved along.
Anyway I just figured it out to use it with Vue Version 2, but for now I am fine with that.

New challenge is at the moment to trigger database trigger every time a client connects via browser.
I found the post:
https://discourse.nodered.org/t/execute-a-flow-when-pages-url-opens-or-refreshes/65783

I can see that everytime a client connects uib sends an object with the information "client connected".
I tried to use the chache example an simply add a function node in front

return { 
    "uibuilderCtrl": "replay", 
    "cacheControl": "REPLAY", 
}

But that doesnt seem to do the job, but what I can see in debug nodes after db query node, that the node gets triggered and the fatched data from the table is there

image

Thank you anyway for your assistance.

The msg you need to watch for is this:

{
   "uibuilderCtrl":"client connect",
   "from":"server",
   "_socketId":"VZ6olW0-YBBw4t4zAAAZ",
   "version":"6.4.1-iife.min",
   "ip":"192.168.1.119",
   "clientId":"tFvVn-A2-AP9rW97b3KSp",
   "tabId":"t368242",
   "url":"new-moon",
   "pageName":"index.html",
   "connections":0,
   "lastNavType":"navigate",
   "tls":true,
   "connectedTimestamp":"2023-08-17T20:35:25.467Z",
   "referer":"https://XXXX:1880/new-moon/",
   "recovered":false,
   "_msgid":"78e871b0ad477b37"
}

If that message reaches a cache node, it triggers a replay event automatically. You can, of course, use it to trigger your DB query. Just remember that it is possible for the client to disconnect itself and then reconnect. For example if the client device or just the browser tab goes to sleep. In those cases, you still get the connect msg but the connections property will go from 0 to 1, etc. and recovered is set to true. So, if you need to (eg if you cached the last query on the client browser), you can avoid triggering the query again.

In my uibuilder application, in order to get a trigger whenever the client connects, I am using this method:

 mounted: function () {
 uibuilder.send({ payload: true, topic: 'load/homedata' });
    }

Isn't this enough for your needs?

Well that will work with Vue. But uibuilder has always sent a client connect message which is triggered on the server as soon as the socket.io connection is made from the client to the server. Timing-wise, this should be more reliable.

It also sends out a disconnect message too.

In newer versions visible/hidden messages are also sent for each connected client should the browser tab go in/out of the users visible display.

You're right, of course.
But the client connect message is general, every time the client connects. With the uibuilder.send message I can send a different topic for every tab I select, so to load specific tab data only when it is displayed. i.e. load/homedata, load/devicesdata, etc...

How to discriminate the specific tab with uibuilderCtrl message?

A fair point, yes, if you want to identify events from within VueJS as opposed to the connect/disconnect and visible/hidden socket.io/browser events, you would have to code that yourself. Then you can still make use of the msg._uib optional build-in meta-data that uibuilder can add to each standard message if you want so that you can still use things like client id, browser tab id, client IP address, page name, etc. to help decisions in Node-RED.

I'm assuming you mean something like a bootstrap-vue tabs component rather than a browser tab in your case.

Vanilla HTML tabs are on my backlog for uib-element, they will have built-in events that will automatically send data back to uibuilder.

I'm not using bootstrap-vue-tabs, but routes, addressed by a side menu. When I click on the menu, the corresponding route is activated and the specific topic is sent by uibuilder.send from that route page.

I spoke about tabs only as a reference to NR Dashboard, but they a routes, not tabs.

Fair enough. Same principle though. They are VueJS features in this case and so need custom front-end code as you say.

sorry for the late reply, tried to read as much docu as possible trying to understand setting up
Vue Js as frontend and Node-Red as backend.

I guess the question was a bit missleading.
I dont want to use the connection of the client to Trigger Query node. I want to acess the data from db by the client browser via button.
With serveral buttons I could the always fetch different tables for example

Moreover I am completely entering a new world with frontend dev in general.
So before I can use it together I need to study how things are working.

Thank you for the response.
Greetz

OK, that is very different from your original followup post. trigger database trigger every time a client connects via browser.

But is still simple. You just need to attach the eventSend function to each button and give each button a unique id, something like this (untested):

<button type="button" id="btn-query1" onclick="eventSend(event)">Query 1</button>
<button type="button" id="btn-query2" onclick="eventSend(event)">Query 2</button>

This will send a message back to Node-RED containing a bunch of information you can use to identify the button and so choose which query to run (use a switch node). When you get the query results, make sure that the output msg still contains the _socketId property from the button output (in case >1 client is connected). Then send the msg back to your uibuilder node. In your front-end code, you can use a uibuilder.onChange('msg', (msg) => { ... }) function to update the appropriate VueJS managed data variables from the new msg which should update the UI for you.

when trying to implement the idea I get:

index.html:

<button id= "fatchPlanes" @click="doEvent" data-mything="Fatch Plane Data" title="Planes">Planes</button>
<button id= "fatchUsers" @click="doEvent" data-mything="Fatch User Data" title="Users">Users</button>
<table id="NRtable"></table>

the id NRtable is then the config parameter in the uielement.
image

When I trigger it with inject nodes all fine!
When I try it with the switch node to figure out witch button was pressed NodeRed goes completely down and I have to restart it.

Error message in the console:

20 Aug 19:19:20 - [red] Uncaught Exception:
20 Aug 19:19:20 - [error] TypeError: node._ui.push is not a function
    at buildUi (C:\Users\mm113\.node-red\node_modules\node-red-contrib-uibuilder\nodes\uib-element\customNode.js:822:14)
    at nodeInstance.inputMsgHandler [as _inputCallback] (C:\Users\mm113\.node-red\node_modules\node-red-contrib-uibuilder\nodes\uib-element\customNode.js:89:5)

I can hear the library screaming dont treat me so bad :slight_smile:

Oooh, that shouldn't happen! And crashing node-red is not your fault at all.

Though you are mixing VueJS with uibuilder's zero-code which isn't a great combination. If you are using bootstrap-vue or Quasar, you should really use those libraries table components and simply pass everything into a suitable VueJS data variable.

Or, if the zero-code stuff is enough for what you want to do, just stick with that.

The error itself is complaining that the uibuilder low-code config isn't an array. Can you please add a debug node to the output of the uib-element node and share at least some of the output? Also, if you can do the same with the link-in node to share the output of one of your db queries?

I suspect that your query output isn't in the format that uib-element wants. It still shouldn't crash node-red though. I will fix that part.

The other issue you have is that you are trying to pass the uib table output into a table tag element. But it doesn't work as you think. Your config will produce a new div element at the end of the existing body. That div will have a duplicate ID. If you want to place your table somewhere specific in more complex hand-crafted HTML, add an empty div with a different ID to the table you are creating and make that the parent.

ouh ok, I will then choose one of the approach.

following debug payload

output uib:

{"payload":{"mything":"Fatch Plane Data"},"_ui":{"type":"eventSend","id":"fatchPlanes","slotText":" Planes ","form":{},"props":{},"attribs":{"data-mything":"Fatch Plane Data","title":"Planes"},"classes":[],"event":"click","altKey":false,"ctrlKey":false,"shiftKey":false,"metaKey":false,"pointerType":"mouse","nodeName":"BUTTON","clientId":"Tg1OFxe9TKzfs0nHHC20O","pageName":"index.html","tabId":"t653900"},"_socketId":"aqNfAys0_WaHksdMAAAN","_msgid":"b71789243deba6b6"}

output query of planes:

{"payload":[{"name":"D-5909","type":"Arcus","manufacturer":"Schempp-Hirth Flugzeugbau"},{"name":"OE-9513","type":"DG-1001M","manufacturer":"DG Flugzeugbau"},{"name":"D-5809","type":"Duo Discus","manufacturer":"Schempp-Hirth Flugzeugbau"},{"name":"OE-5663","type":"FOX","manufacturer":"MDM-1"},{"name":"OE-5280","type":"ASK 21","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-5664","type":"LS8","manufacturer":"Rolladen-Schneider"},{"name":"D-6521","type":"LS1","manufacturer":"Rolladen-Schneider"},{"name":"D-3578","type":"LS4","manufacturer":"Rolladen-Schneider"},{"name":"OE-5338","type":"LS4","manufacturer":"Rolladen-Schneider"},{"name":"OE-5563","type":"LS4","manufacturer":"Rolladen-Schneider"},{"name":"OE-5211","type":"ASK 13","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"D-3987","type":"ASK 13","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"D-8525","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-5287","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-5295","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"OE-0772","type":"Ka 8","manufacturer":"Alexander Schleicher Flugzeugbau"},{"name":"D-KNAM","type":"SF-25C Falke","manufacturer":"Scheibe"},{"name":"OE-ARD","type":"Husky","manufacturer":"Aviat Aircraft"}],"_ui":{"type":"eventSend","id":"fatchPlanes","slotText":" Planes ","form":{},"props":{},"attribs":{"data-mything":"Fatch Plane Data","title":"Planes"},"classes":[],"event":"click","altKey":false,"ctrlKey":false,"shiftKey":false,"metaKey":false,"pointerType":"mouse","nodeName":"BUTTON","clientId":"Tg1OFxe9TKzfs0nHHC20O","pageName":"index.html","tabId":"t653900"},"_socketId":"aqNfAys0_WaHksdMAAAN","_msgid":"b71789243deba6b6","query":"SELECT name,type, manufacturer FROM dbo.planes","queryMode":"query","queryParams":[],"sqlInfo":[],"_event":"node:97d3349461e975f0"}

Ah, right. I can see. The message you send back from your DB queries contains a msg._ui property already. But it isn't an array.

I think that is my fault. It is, of course, the output from the eventSend function and it is just an object not an array.

Perhaps I should have chosen a different msg property for the outputs.

Please delete the output msg._ui. Put a change node on the output from uibuilder. It should do 2 things. First, set msg.button (or some convenient property) to be msg._ui.id, then delete msg._ui. You can do that with a single change node.

NOTE: You may need to set the "deep copy" flag as shown above. Try without first.

Then your flow should work when you feed that into the uib-element node.

I have added a fix for the crash into the next release of uibuilder. I will also need to add a check to see if msg._ui.type exists - or maybe add another property to differentiate output _ui properties from input ones.

Well done for finding an important bug in uibuilder :grin::

ouh ok. Even it was not my itention, happy that I found something :slight_smile: thanks for your eyes on that.

Your proposal, using a change node (without "deep copy value true) leads now also to the fact that I can see the data flowing to the client side (seeing that in the browsers dev tools, without change node this was not the case).

I asume the wiring should be fine now. The reason why I use "should" = cant see the table yet.

For my understanding I have to do following in detail:

  • Declare a vue variable array e.g items with
const app = createApp({
    // Define Vue reactive variables
    data() { return {

        items: [], //table data
        

    } },
  • Every time a msg is sent from NodeRed the onChange function fills the array with the values in
uibuilder.onChange('msg', (msg) => {
            console.log('>> msg recvd >>', msg.payload, this)
            app.items = msg.payload;

        })
  • last thing would be using the binded variable and render the table by iterating through the items
 <table class="striped hover small">
                <tr v-for="items in items"></tr>
              </table>

Since I decided to Use VueJs 3 without the zero code elements I guess I messed up the syntax somewhere between the working example from vue js version 2.

Complete files:
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="../uibuilder/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/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 class="with-subtitle">Simple Vue v3 App</h1>
        <div role="doc-subtitle">Using the uibuilder IIFE library.</div>

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

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

        <div class="border">
            <p>
                Not sent back to Node-RED - this just shows that VueJS is working
            </p>
            <p>
                <button @click="count++" title="Not sent back to Node-RED - this just shows that VueJS is working">
                    Simple VueJS Counter: {{ count }}
                </button>
            </p>
        </div>
        
        <div class="border">
            <p>
                This is sent back to Node-RED.
            </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 class="text-smaller">
                Note that including an event button in a form will automatically send all
                of the form inputs back to Node-RED as well.
            </p>
        </div>
        
        <p>
            <label for="input1">This is sent to Node-RED when you exit it: </label>
            <input v-model="input1" @change="doEvent" id="input1" type="text" placeholder="Type text then exit field">
        </p>

        <button id= "fatchPlanes" @click="doEvent" data-mything="Fatch Plane Data" title="Planes">Planes</button>

        <button id= "fatchUsers" @click="doEvent" data-mything="Fatch User Data" title="Users">Users</button>
             
        
        <table class="striped hover small" :items="items" sort-by="ROWID"></b-table>

            <table class="striped hover small">
                <tr v-for="items in items"></tr>
              </table>
        
    </div>
    

</body></html>

index.js:

// @ts-nocheck
'use strict'

//import { createApp } from 'vue'


const { createApp } = Vue



// 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({
    // Define Vue reactive variables
    data() { return {

        items: [], //table data
        

    } },

    // Dynamic data
    computed: {},

    // Supporting functions
    methods: {

        // REALLY Simple method to return DOM events back to Node-RED.
        doEvent: (event) => uibuilder.eventSend(event),

    },

    // 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.payload, this)
            app.items = msg.payload;

        })
    },
})

app.mount('#app')

You may wish to note that I've just pushed uibuilder v6.5.0 which includes a comprehensive set of fixes for the msg._ui issue you found. If you update to this version, you don't need to remove the output _ui property since all of the nodes, including uibuilder itself will automatically strip a version of the property coming from the client.

1 Like

yes can confirm that. With new version it works now without change node now.

1 Like

reading further examples the table rendering must be look something like:

          <table >
                <tbody>
                  <tr v-for="items in items" :key="items.name">
                    <td> {{ items.name }}</td>
                    <td> {{ items.type }}</td>
                    <td> {{ items.manufacturer }}</td>
                  </tr>
                </tbody>
            </table>

still no luck with rendering the table correctly.
With
console.log('>> items >>', app.items, this)
I can see the variable is populated with the values.
Do I miss something?