Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

philbenoit's avatar

VueJS - Update dom after nested array updates

I am trying to add a property to an object within an array after the page has loaded, and it works. The issue I have is the object not being rendered to the page.

The example is a list of airports to display with a button to "get dates", upon this returning from the server show the list of dates for this specific airport in the dom.

It works if the airport has an empty array for dates but this is not guaranteed from the server response.

Using vue how can I write the html v-for to account for an object property array that will be provided sometime in the future?

the js fiddle to help

0 likes
12 replies
philbenoit's avatar

@xdimension thanks for the reply, unfortunately the back end system we are working with does not give us the dates included with the data. In addition the airports array might contain over 200 airports, loading the data from each via the API will take too long.

The situation I am trying to achieve is that we show the airports and when a user takes action to select one we then load in the data for this. The date array by itself could contain a large amount of data relating to not only date but also flight info etc.

I guess I should have been more specific, I would like to know if its possible to have vue render an array thats not there on page load but is added sometime in the future.

tjames's avatar

@philbenoit There's two issues with your current code:

  1. For vue to iterate over a list/array, the array has to actually exist, even if it's empty. When you're initializing your airports data, you're only adding the name property, so when you're trying to iterate over airport.flightTimes in the v-for, vue just ignores it because that property doesn't exist, and therefore is not watching it for any changes.

  2. In your getFlightTimes method, while you're retrieving the times, you're only adding those times to the airport object that was passed from the click event, not the airport in the instances data object.

Here's a working example of your code with tweaks: https://jsfiddle.net/bhf1fLnt/

1 like
xdimension's avatar

@philbenoit Yeah, my idea was adding flightTimes to airports data so Vue will render to DOM when it's changed like @tjames explained in point 1.

philbenoit's avatar

@tjames @xdimension thanks for your input.

I have worked through both of your methods and agree they are working.

@tjames this comment is the heart of the issue "vue just ignores it because that property doesn't exist, and therefore is not watching it for any changes." I would like to add a property to the array at a later date and then have vue watch and render it, perhaps by forcing a watcher or something similar on the vue instance at that time.

The example I provided was a slimed down version for explanation but it seems I have made it too simple. The airports array is coming back from an ajax call and could contain an unknown number of airports, could be 100's. Adding the property in the return will take additional time that I'm trying to avoid.

Side note: the point 2 above about only adding to the object passed in appears to be incorrect in my testing. When adding the dates to the object and then logging out the original this.airports the updated dates are contained within the unique airport, I would have agreed with you if I had not seen it come back like this.

Thanks for all the help

tjames's avatar

@philbenoit I should've clarified #1 a little better. It's not that vue isn't watching the change, it is, since the data object is changing, however it's not rendering the way you want it to because of how Vue's reactivity works.

The problem is that you're initializing/adding data without a property and then trying to add that property manually after the fact which won't work with Vue's system, as watchers are only added upon initialization. This is explained in Vue docs: http://vuejs.org/guide/reactivity.html - read the Change Detection Caveats section. The exception is if you either replace the object with a new object, i.e. using something like Object.assign(), or you use Vue's set/$set methods. Otherwise you need to initialize your data with all the properties you want it to watch - they don't have to contain data, but they have to exist. Using set/$set will work fine if you are only adding a single property and know what it is, but would be pretty cumbersome if you're adding a bunch of new properties.

To address your comment about the airport array coming back with unknown number of airports: That doesn't matter, the number of results is irrelevant, what matters is that each 'airport' object contains the properties you want to watch UNLESS you are manually adding the properties using vue's set/$set methods. See this fiddle for a commented example: https://jsfiddle.net/dhcj34mc/1/

1 like
philbenoit's avatar

@tjames Thanks so much for the input, it does make sense just need to look at how we can update the data to add in this property.

I know it tricky but I was assuming this would be a common issue in the world of API's and lazy loading only whats needed. I dont believe its the job of the API to provide an empty array for every possible relationship that might be called on a model so the thinking is the front end must set this up at a high cost to the user.

Thanks for all the input!

tjames's avatar

@philbenoit maybe it's just me but I'm having a hard time wrapping my head around why this would add any cost, or have to be 'added in'. From what you're describing, it's not like you're trying to add dynamic unknown properties at runtime, you're just fetching data at a later point. If you're wanting to fetch just the airport names, and then fetch dates at a later time, that's fine, but you need to structure things differently on the frontend to support that flow. My input/responses above are purely related to the code presented and why a nested v-for doesn't work when data isn't initialized.

To be honest, what you're trying to do would be trivial to manage using isolated components for both airports and flight time listings. Then you can structure things based on what your api returns and how you want to interact with it.

philbenoit's avatar

@tjames This is a fuller description of the process, I had to use an airline example for disclosure reasons.

A user hits a search page and adds their location (or browser location), they search for a list of airports. This return from the API is only data about the airports, their location and other details including a list of available airlines flying from this airport. This airport and the airlines for each are displayed to the user.

The user then selects an airline from a given airport to fetch the dates available from that location using the ID of the airport and the ID of the airline. This is returned and needs to be shown to the user to select their journey. They could, without leaving the view select another airline to view their dates should they wish.

The data for each airline at each airport does not include the dates as this could increase the initial return by factors or time. I was trying to avoid having to implement an empty array for each returned airline on return from the server as there could be many airports and many airlines at each.

I know it will take a fraction of time but was looking for a solution where I could alert vue to a new addition to the object and have it render these new results.

tjames's avatar

@philbenoit That's a pretty common scenario and I can think of a few ways to implement it off the top of my head. Here's a modified version of your code that populates the airport data first and upon selecting an airport, calls the api to get dates (and whatever else): https://jsfiddle.net/pztvntvb/1/

philbenoit's avatar

@tjames Thanks, I understand this setup, its the nested setup I am requiring, where the dates are displayed within the looped airport html block, not separate. All good and thanks for the help.

anonymous's avatar

I do not exactly understand your use case, but if you just want a quick hack, for any property, try this...

https://jsfiddle.net/1urcq7bk/3/


<ul v-if="setPropertyIfEmpty(airport,'flightTimes',[])">
    <li 
        v-for="dates in airport.flightTimes">
            {{dates.date}}
    </li>
</ul>


methods: {
    setPropertyIfEmpty: function(obj, prop, value) {
        if(!obj[prop]) Vue.set(obj, prop, value);
        return true;
    },
}

here is an example of a possible nested property using a filter

https://jsfiddle.net/1urcq7bk/4/

  <div v-for="airport in airports | addPropertyIfEmpty 'flightTimes' []">
    <ul>
        <li v-for="dates in airport.flightTimes">
            {{dates.date}}
        </li>
    </ul>
</div>      

    filters: {
        addPropertyIfEmpty: function(items, prop, value) {
            items.filter(function(item,i) {
                if(!item.flightTimes) Vue.set(items[i], prop, value);
            })      
            return items;   
        },
    },

Please or to participate in this conversation.