Create a template table from message object

@TotallyInformation, @bakman2

I have a table working now both with a Vue based UIbuilder version and a jquery UIbuilder version (I figured I better get both working to figure out what I prefer). I'll post the code to get your feedback or maybe help others. It's nothing special, but at least it's another example.

Before I do so, I have a couple questions so maybe I can make a couple tweaks:

Taking @bakman2 suggestion, I flattened my object to an array of objects.

{
	"devices": [
		{
			"serial": "1a",
			"channel": 0,
			"status": "running",
			"value": 100,
			"setpoint": 200
		},
		{
			"serial": "1a",
			"channel": 1,
			"status": "running",
			"value": 100,
			"setpoint": 200
		}
	]
}

This made creating a table much easier, but is there really no downside to this format versus my original object:

{ "serialNum": {
            "1a": {
                    "channel": {
                        "0": {
                            "status": "running",
                            "value" : 100.0,
                            "setpoint": 200.0
                        },
                        "1": {
                            "status": "running",
                            "value" : 100.1,
                            "setpoint": 200.1
                        }
                    }
                },
            "2a": {
                    "channel": {
                        "0": {
                            "status": "running",
                            "value" : 200.0,
                            "setpoint": 300.0
                        },
                        "1": {
                            "status": "running",
                            "value" : 200.1,
                            "setpoint": 300.1
                        }
                    }
                },
            "3a": {
                    "channel": {
                        "0": {
                            "status": "stopped",
                            "value" : 300.0,
                            "setpoint": 400.0
                        },
                        "1": {
                            "status": "stopped",
                            "value" : 300.1,
                            "setpoint": 400.1
                        }
                    }
                }
            }
    }

Maybe I do not understand objects like I thought I did. I would have guessed my original object would be desirable because I could quickly drill down to a value of a specific channel on a specific board. For example the program would just quickly skip over all the objects that do not match the desired serial number. In the new object the program has to potentially loop through every single object in the array to find what I am looking for. For making a table, especially a sortable table, I suppose it does not matter because every row is basically an object. Maybe it just depends on the final use case how to structure the object.

My other questions relate to Vue:

  1. Should there only be a single var app1 = new Vue({ // The HTML element to attach to el: '#app', Or might there be a case for multiple of these? I ask because it seems my entire html <body> will be encased in <div id="app">

  2. In looking at @TotallyInformation complex home dashboard Vue app I see there are many Vue components. Even though they are outside of the initialized new Vue()Are those able to make use of the msg object because they share dtOpts like this?

data: function() { return {
        // For formatting dates and times
        dtOpts: {
            timeZone: 'Europe/London',
            weekday: 'short', month: 'short', day: 'numeric',
            hour: 'numeric', minute: 'numeric',
        },

Thanks for the help, I think I am close to understanding the setup and can now just have fun building what I want.

Yes, you've hit the nail on the head!

Indeed, you could also split the difference, at least when using bootstrap-vue, that is to use an object of objects instead on an array of objects.

e.g.

const devices = {
	"1a-0": {
			"serial": "1a",
			"channel": 0,
			"status": "running",
			"value": 100,
			"setpoint": 200
	},
	"1a-1": {
			"serial": "1a",
			"channel": 1,
			"status": "running",
			"value": 100,
			"setpoint": 200
	}
}

That still lets you drill down but still lets you pass the data without change to the table component in bootstrap-vue (the docs say it does anyway, I've not tested this particular data structure).

Another alternative is to send each device/serial object separately from NR and accumulate it into an array in the front-end instead.

So many options! :woozy_face:

Clearly there are downsides to each of these approaches and, as you say, the "best" depends on how you are going to use the data.

I don't know that any of us know them as well as we think we do! While your original object does indeed facilitate drill-down, it is also very complex.

When you move to the array format, you can no longer do myobject["myindex"] but have to do some kind of filter or lookup which personally I find a pain to do.

Using the intermediate form still lets you drill down a couple of levels quite easily with some text joins to create the index. If you need to turn it back into a pure array, that is also quite easy using Object.values(myobject) mdn.

image


  1. While you can certainly have >1 Vue app, generally people don't. The only restriction is that each app has to only be defined by a single tag. So everything else has to exist as children of that tag. I believe that you can even make the app tag the <html> in which case, you can use Vue to control things in the head as well as the body. But again, unusual.

    So it isn't unusual to effectively make your whole body a single div that is the app. While you can attach the app to the body, it would then include things like your script links which is a risk because Vue could overwrite things a bit too easily.

    When you get more advanced, you will find that you tend to move more and more stuff out of your index.html file into separate Vue files which can make your app a lot easier to manage, especially if you need several people to work on it. That's when the single div approach makes more sense.

  2. Eventually, I would move those component definitions into their own .vue files and either load them dynamically (using a helper library) or, for efficiency, by including a "build" stage using something like webpack to effectively "compile" each component and the main app into more efficient code.

    I wrote that as I was still learning Vue (which I am still doing certainly) so I was (and still am) just dumping code into 1 place. I've since learned how to load .vue files using a helper without needing a build step (documented in the WIKI) and eventually, I'll change the examples accordingly since they are then much simpler to follow.

    The components are indeed able to make use of the msg object because that is part of the uibuilder object which is global! So any component that you have loaded works exactly the same as the main app as far as uibuilder is concerned. With a single exception - that is that the components don't need to do uibuilder.start(), that only needs to happen once in the main app (which always runs first).

    You could also only process the msg change in the main app but then you have to pass the resulting data "down" to the components. To do that, you have to use something called "props" which I found confusing to begin with. Props | Vue.js

    Personally, I find the uibuilder object easier to use and reason about so I create a uibuilder.onChange() function in each component. I think that the efficiency of the event processing in JavaScript is so efficient, this works really well.

the main advantage is that it just works with the ui-table widget :-)... ok ok - I'll stop now !

3 Likes

@dceejay haha fair enough.

My "dream" is to have a pretty robust app built similar to @TotallyInformation so I still think I'll need to go full custom.

1 Like

Hi all,

I've come a long way. I basically have bootstrap-vue tables with sorting working and it's pretty awesome. I still owe you a basic demo flow so maybe someone else can use it as a starting point.

One final thing I'm not fully understanding with UIBuilder itself is how the UIBuilder node stores the index.html, index.js, etc. files. A couple times I somehow managed to delete my index.html file but I have no idea how. Obviously I did something wrong, just not sure what. One time it seemed like I rerouted an output from the UIBuilder and that reset my index file, but surely that was some sort of coincidence. Are the src files stored in my .node-red directory?

Thanks!

1 Like

No magic :smiley:

Everything is kept in the server's filing system. So you can use normal backup methods to take copies, you can even use git if you like.

Eventually, I'll build a simple git interface into uibuilder so that if an instance has git initialised, you can do simple commits, undo's and pushes.

It should be pretty robust, it isn't doing anything especially clever with the files and I've not had any issues myself either on Windows or the Pi.

There is a flag that, if you delete one of the index files, automatically copies the default template over. You might disable that so that it is a bit more obvious when the file gets deleted?

The only way that could happen is if you have a file-out node of some kind that writes to the file. Otherwise, the only thing that will change any of those files is the editor interface which works off a set of dedicated API's.

Yes, assuming that you are not using Node-RED's projects feature and are using the default Node-RED installation, the uibuilder root folder will be in ~/.node-red/uibuilder. Then each instance of the uibuilder node creates a sub-folder named after the URL setting. Then that folder has the files in the src folder. So an instance with a URL called fred will have its files in ~/.node-red/uibuilder/fred/src/. There is also a dist folder that will take preference if index.html exists in it. That is in case you want to have a build step for compiling things like Vue or transpiling with Babel.

Ah, that makes me wonder - have you changed the url setting at all? If you do, at the moment you get a new folder with the default template files. Your original files will still be available under the old name. Similarly, if you add a new instance with an old name, the old files will show up. I realise that this can be somewhat confusing and a proper rename feature is in the works.


I actually do most of my web development on my dev machine - so the "server" filing system is actually local. I tend to use VScode to edit the files as it is a lot more powerful than the ACE editor that Node-RED makes available. VScode now also has the ability to edit over SSH so you could use it with a Pi, etc if you prefer a full IDE instead of uibuilder's editor. I do use the uibuilder editor, typically for quick tweaks.

hi, i would like ask about this dashboard implementation. How it possilble to do a detailed table in a node-red app?

Hi, can I suggest that you start a new topic - it makes it a lot easier for people to help.

In the new topic, you will want to tell us a bit more about how you want things to work.