UIBuilder - VueJS not reacting to modifed data

I’m building a UI using UIBuilder with Vue, Bootstrap-Vue. A weird issue has cropped up; I’m not sure if its a issue with my code, UIBuilder, Vue, Bootstrap, etc. The UI code has gotten a bit complex, so rather than posting all the code, flow, etc I’m going to try to describe whats happening and post code excerpts.

The hardware system is Rasp Pi 4 with Node Red, UIBuilder, Vue, Bootstrap-Vue, and a PLC, which communicate over ethernet using MQTT & UDP. The data is being communicated as expected, and in fact the system works fine until I introduce the Computed property code described below.

I am new to UIBuilder, Vue, etc. I started the UI with simple features, adding more as I learned and correcting mistakes with the previous features.

The UI consists of Radio Buttons that set a value to be used as a data filter, and several b-tabs each with tables that list the filtered data.

The PLC currently sends two data arrays to Node Red, both Arrays of JSON Objects. These arrays are stored in Global Context. In earlier, simpler versions of the UI, these arrays simply loaded into elements.

On RowSelect, a copy of the data in the selected row is sent to an editable form and a "Save" button sends the modifed data from the form to the PLC via uibulder.send. The PLC receives the modified data, and sends it back to Node Red which updates the Global Context and sends the update to UIBuilder. UIBuilder/Vue/Bootstrap reactively updates the associated table as expected. So far, its all good. To be clear, I'm not modifying the original data array but copying that data from the selected row to a form. The update to the array occurs when Node Red receives it from the PLC.

I then added filtering to the tables. Initially, I used filtering property with a filter-function. All worked fine with this also, but I needed one more level of filtering. What I ended-up doing was writing a Computed property to build a new, filtered array and then filtering again with a second property using filtering property. All is working fine at this point: data can be edited, saved/sent to the PLC, received back, global context is updated and table in UI reactively updates.

You'll see in "mounted > uibuilder.onChange" the two arrays of JSON objects. The data is different, but the functionally is essentially the same. When the Radio Button is selected, both arrays need to be filtered by the same property using a Computed property. Then, each filtered array is then loaded into a separate table. Both arrays are edited and save/send the modifications to the PLC the same way, and the PLC returns the data to Node Red which stores it in Global Context. The only difference, other than the data itself, is that arrScene is filtered again using filter-function as described above.

Filtering arrSBank using Computed property to create the filtered ftrSBanksByZN array is were the issue is introduced, but I'm not sure why. With both data arrays filtered using their Computed property, when I edit/save/send the data in the ftrScenesbyZN array (filtered version of arrScene), the tbSNlist table does not reactively update with the modifed data. The modifed date is being sent to the PLC, the PLC is sending it back and Node Red is storing the updated data in Global Context. Removing the second filter (b-table filter-function), doesn't eliminate the issue. Neither the arrScene array or its filtered znScene array is updating with the modified data.

Here's where it gets weird. If I remove the Computed property for ftrSBanksByZN, and change the :items in tbSBlist to arrSBanks (the unfiltered array), then the data for arrScene reactively updates as expected, as does the Computed property ftrScenesbyZN. If I add back the Computed property for ftrSBanksByZN, the arrScene does not update (but the updated data is in the Global context). To be clear, the Computed property ftrSBanksByZN seems to be triggering the issue with arrScene not updating but I haven't figured out what the nexus is.

I think the issue may be in "mounted > uibuilder.onChange" but I'm not sure. Maybe its an issue with my coding, or maybe a bug in bootstrap-vue.

(Code Excerpts)

<!-- Table Scenes-->
<b-table	id = "tbSNlist"
		selectable select-mode="single"
        :items="ftrScenesbyZN" 
        :filter="tab" :filter-function="filterDevice"
        @row-selected="onSelect">
</b-table>

<!-- Table Sbank-->
<b-table	id = "tbSBlist"
		selectable select-mode="single"
		:items="ftrSBanksByZN "
		@row-selected="onSelect_sb">
</b-table>


var app = new Vue({
    // The HTML element to attach to
	el: '#app',
	
	data() {
		return {
			arrSBank:[],
			arrScene: [],			
		};
	},
	
	computed: {
		ftrScenesbyZN: function () {
				let scenz = this.arrScene; //data array to be filtered
				let znSelect1 = this.znSelect[0]; //property to be filtered, based on radio button selection
				znScenesA = scenz.filter(scene =>{
					return scene.zone == znSelect1;
				});
				return znScenesA;
		 },

		ftrSBanksByZN : function () {
				let sbnk = this.arrSBank; //data array to be filtered
				let znSelect2 = this.znSelect[0]; //property to be filtered, based on radio button selection
				znSBanksA = sbnk.filter(bank =>{
					return bank.sw_zone == znSelect2;
				});
				return znSBanksA;
		  },
	},

	mounted: function() {     // Called when Vue is fully loaded
		
		uibuilder.onChange('msg', function(msg) {
		// Triggered when the node on the Node-RED server recieves a (non-control) msg
			app.arrScene = msg.scenes; 
			app.arrSBank = msg.sbanks;
		});
	},
});

Hi, hope you don't mind but I tweaked the title to show that it is VueJS that isn't responding rather than uibuilder.

Really, this is more of a VueJS question & I'm not actually that expert at VueJS so I'm not sure I can really help that much I'm afraid.

Usually, problems with reactivity are due to handing complex objects that haven't been defined as such up front (in data). In those cases, Vue can't detect that something deep inside the object has changed and therefore doesn't respond. There are various ways to avoid this issue including a function that forces Vue to understand what is changing. You will find the documentation for these on the Vue docs site though it may take you a few readings to fully understand - it certainly did me and I still don't really know if I actually understand it properly. :frowning:

No problem, and thank you for responding. I did see the "reactivity in depth" article, but declaring the properties didn't work. I wasn't sure if it was a vue issue or what so I thought I start here. The weird thing is the subject data updates if I remove an mostly unrelated computed property.

Hello ..

would it be possible to share some sample data ? (enough to show the issue when filters are applied)
also i dont see in your code a methods section with the onSelect and onSelect_sb functions or the radio buttons. please share a more workable code excerpt in order to try to replicate this

FYI: I'm using the latest uibuilder version with Vue and it works fine for me but I had a similar issue with apexchart v3.30.0 not rendering. I went back to v3.22.2 and this solved it.

Thank you for offering to replicate the issue. I was only showing excerpts of the code, becuase the way data bounces back and forth between devices I thought it might be challenging to replicate. But acutally I guess one could use Inject nodes. I think I may refactor the code first, and possibly try writing the computed properties differently to see if that works (i'm not confident about using fat arrows and other short-hand)

1 Like

I'm not sure but In your function mounted there is no uibuilder.start()

	mounted: function() {     // Called when Vue is fully loaded
		
        uibuilder.start()

		uibuilder.onChange('msg', function(msg) {
		// Triggered when the node on the Node-RED server recieves a (non-control) msg
			app.arrScene = msg.scenes; 
			app.arrSBank = msg.sbanks;
		});
	},

Now best practice to put the start into the created property instead. Also best practice to include this as a sole parameter to start because that tells the uib library that you are using VueJS and gives it access. Then you can use things like the Toast notification helper

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

        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.

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

Assuming, of course, you are using a current version of uibuilder :grinning: