Uibuilder - loading data on page load

Another newbie question, but I don't see this basic thing in the examples (it may seem obvious to an expereinced web programmer, but its not to me.)

How do I get Node Red to inject a payload when a client browser first loads an uibuilder page? In the examples, there's typically an inject node to send the payload. But, I was some data loaded when the page opens.

If you notice, the uibuilder node has two outputs ..
the top one is for messages you send from your front-end using uibuilder.send()
the bottom one is for control uibuilderCtrl msgs.

Wire a Complete message Debug node to the bottom output to see what messages you get.

image

You can use any of those msgs (client connected or ready for content) to trigger some logic in NR and prepare the data you need and re-wire that set of nodes back to the input of your uibuilder node in order to send it to your front end.

could use a function node with:

if (msg.uibuilderCtrl === "ready for content") {
    msg.payload = "my data"
    return msg;
}
1 Like

Really, you should use the message from the client. I've fixed that in the next release of uibuilder and it will only output a client control message because the server one is only needed by the client so it shouldn't appear on port #2.

Thanks, but this isn't working. Not sure what I'm doing wrong. The flow is below. If I inject the payload, it loads into the page. When the page loads, the function outputs the correct payload to a debug node. But, when loading the page the payload does not load

[{"id":"058ba9b7f3b37353","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"58a97cfca95999ff","type":"uibuilder","z":"058ba9b7f3b37353","name":"","topic":"","url":"home_config","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"vue","extTemplate":"","showfolder":false,"useSecurity":false,"sessionLength":432000,"tokenAutoExtend":false,"reload":false,"sourceFolder":"src","credentials":{},"x":570,"y":260,"wires":[["0fa306891c3a3a1f"],["5e4a8ba26f24c4e1","5cd89f4b168a368d"]]},{"id":"0fa306891c3a3a1f","type":"debug","z":"058ba9b7f3b37353","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":260,"wires":[]},{"id":"5e4a8ba26f24c4e1","type":"debug","z":"058ba9b7f3b37353","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":320,"wires":[]},{"id":"5cd89f4b168a368d","type":"function","z":"058ba9b7f3b37353","name":"","func":"if (msg.uibuilderCtrl === \"ready for content\") {\n msg.payload = global.get('tbSN');\n return msg;\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":580,"y":400,"wires":[["58a97cfca95999ff","0b43a96346fd82c2"]]},{"id":"c921d4ccad028fbf","type":"inject","z":"058ba9b7f3b37353","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"tbSN","payloadType":"global","x":400,"y":340,"wires":[["58a97cfca95999ff"]]},{"id":"0b43a96346fd82c2","type":"debug","z":"058ba9b7f3b37353","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":880,"y":420,"wires":[]}]

<!DOCTYPE HTML>

<html lang="en">

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

		<title>Home Configuration</title>
		<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 style="font-size:.9em" class="nr-dashboard-theme">

			<h1>Home Configuration</h1>
			<pre id="msg" class="syntax-highlight">Waiting for a message from Node-RED</pre>
			
			<div id="app" v-cloak>
			  <b-container id="app_container" class="mt-5">
				<div class="mt-5">
				  <b-table striped hover small :items="payload" sort-by="id"></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>
var app = new Vue({
    // The HTML element to attach to
	el: '#app',
	
	data() {
		return {
			 payload: []  // 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() {
        // **REQUIRED** Start uibuilder comms with Node-RED
        uibuilder.start()
    },

    // Called when Vue is fully loaded
	mounted: function() {
		// Keep a convenient reference to the Vue app
		var vueApp = this

        /** Triggered when the node on the Node-RED server
         *  recieves a (non-control) msg
         */
		uibuilder.onChange('msg', function(msg) {
			vueApp.payload = msg.payload; // this is what grabs the msg.payload when injected to UI_Builder
		})

		// Send message back to node-red
		// uibuilder.send({payload:'some message'})
	},
	
}) // --- End of the Vue app definition --- //

Those don't look right. You need to share the JavaScript not just the HTML. You wouldn't normally recreate a msg variable in the Vue data so it is likely that you are still using the wrong variable. You should be passing the msg.payload from Node-RED to a suitable Vue data variable in your uibuilder.on('msg', function(msg) { ... }) function.

Thank you for your continued help. I added insult to injury with that previous post, as there was some legacy html markup. I've edited the above post with cleaned-up mark-up, including the js file. That removes the v-for="item in msg.payload from the previous post.

The inject node works to build the table. The "ready for content" function outputs the payload to the debug node. But, that payload is not building the table in uibulder when the page is "ready for content."

What is the content of global.tbSN?

The reason it isn't working is that you have tried to send back a control msg which uibuilder ignores - if it didn't, you could get into all kinds of looping and security issues.

I thought that I output a warning when you tried to do that but it seems to have disappeared somewhere - I will add to the backlog.

Build a new message rather than trying to reuse the control msg.

if (msg.uibuilderCtrl === "ready for content") {
    let newMsg = {}
    newMsg.payload = flow.get('tbSN')
    newMsg._socketId = msg._socketId
    return newMsg
}

Note how I've created a new msg object and how I've also copied over the socket id - without that, the message would be broadcast to all connected clients. With it, it will only be sent to the originator.

I've also tidied your flow to a more normalised version which is easier to follow.

image

The link nodes remove the difficult to follow loop wires and moving the function to the input means that it is more obvious what it does.

1 Like

That worked! Thank you.

I think this is where http_in & http_out nodes are a bit more intuitive to me, but I appreciate the work you've shared with the community. Now I'm off to re-watch / read vue tutorials.

1 Like

Mostly, you can think of the uibuilder node as an http-in and an http-out node joined together. Kind of like imagining those nodes "inside-out". With them, you set up an end-point (a url) and anything that hits the endpoint goes through a flow and ends with a returned output.

With uibuilder, the node sets up the endpoint (the url) but rather than having a single response as you would with http-in/out, the response is your web code (html, css, js) and all the good stuff then happens over the websockets connection that is built in. To do something similar with http-in/out, you would also need a websockets-in/out. uibuilder can also deliver multiple pages for a single node if you want whereas, with the other nodes, you need a pair of nodes for each "page" or you need to create a url wildcard pattern. We might get wildcard patterns on uibuilder one day but it isn't a high priority.

So to build a data-processing flow around your page with uibuilder, it goes on the "outside" with the output ports used to trigger something that loops back to the input and visa-versa. Whereas with the http nodes, the processing goes on the "inside" between the 2 nodes. Link nodes are fantastic to make the uibuilder flow logic easier to visualise and to allow inputs from other parts of your flows. A new node, uib-sender will be coming with the next major release of uibuilder that will also let you send messages to your front-end and get back responses without having to push everything through to the main node. That will also be the start of allowing 3rd-party nodes to communicate with uibuilder instances so that Dashboard-like behaviours can be built.

1 Like