Home » vue » Can't update the v-for list when ajax update the data

Can't update the v-for list when ajax update the data

Posted by: admin November 26, 2021 Leave a comment

Questions:

I using a datepicker and using javascript to pass the date to the vue instance, but after the first time render the list using v-for, when i call the function with other date, it keeps the previous one data on display.
Here is the function i have:

function updateDate(fdate) {
var datefieldVal = document.getElementById('date').value;
var locationfieldVal = document.getElementById('location').value;
if (datefieldVal && locationfieldVal) {
   var tslot = new Vue({
        el: '#time-slots-container',
        data: {
            slot: {}
        },
        mounted: function () {
            this.fetchTimeslot();
        },
        methods: {
            fetchTimeslot: function () {
                var that = this;
                $.ajax({
                    type: "GET",
                    url: timeslotApiURL + "?date='" +fdate+ "'",
                    contentType: "application/json;",
                    dataType: "json",
                    success: function (data) {
                        that.slot = JSON.parse(data.d);


                        setTimeout(showTimeSlotAndButton, 500);
                    }
                });

            }
});
}
} 

anyone have idea, how to update the v-for list using the new slot data? Thanks

Answers:

assuming your data is coming back correctly, it’s probably just that the slot is not reactive. Try:

success: function (data) {
  Vue.set(that.$data, 'slot', JSON.parse(data.d))
  ...

Looking at the above code you should avoid creating and mounting a new instance every time you want to update the date. I’m unsure what Vue would do in this scenario but it may just fail as you can’t mount another instance to an existing one.

Instead move all your Vue code outside of the updateDate function.

Then if you need to prevent the v-for from firing until you have the data, just add a data param of dateReady and set this to true when you have completed the ajax request, i.e.

data: {
  slot: {},
  dateReady: false,
},
...

success: function (data) {
  Vue.set(that.$data, 'slot', JSON.parse(data.d))
  that.dateReady = true
  ...

then you can just add v-if="dateReady" to your v-for element:

<div v-if="dateReady" class="time-slot-row" v-for="hourly in slot.TimeSlot">

how you pass fdate to the ajax request is now handled differently, but you could have something like:

var datefieldVal = document.getElementById('date').value;
var locationfieldVal = document.getElementById('location').value;

if (!datefieldVal || !locationfieldVal)

	return
    
var tslot = new Vue({
    el: '#time-slots-container',
    data: {
        slot: {},
        dateReady: false,
    },
    mounted: function() {
        this.fetchTimeslot();
    },
    methods: {
        fetchTimeslot: function() {
            var that = this;
            $.ajax({
                type: "GET",
                url: timeslotApiURL + "?date='" + that.getDate() + "'",
                contentType: "application/json;",
                dataType: "json",
                success: function(data) {
                    that.slot = JSON.parse(data.d);
                    that.dateReady = true
                    setTimeout(showTimeSlotAndButton, 500);
                }
            });

        },
        getDate () {
            // whatever you do here
            return ''
        },
    },
})

// wherever else you are using this...
tslot.fetchTimeslot()

Essentially you move more of the logic into Vue’s instance, i.e getDate() if that isn’t possible then you could set up a param to hold the date you want to fetch, use that in the ajax call and create a new method to update it. Lots of ways around it but the main issue is ensuring you only have a single instance of vue bound to that #time-slots-container element

###

here is the v-for code:

                <div class="time-slots-list-container">
                <p class="description"><%=GetGlobalResourceObject("OPTBSRES","SelectedStartTime") %> <span v-if="slot.SelectedSlot==null"><%=GetGlobalResourceObject("OPTBSRES","ToBeSelect") %></span><span v-if="slot.SelectedSlot!=null">{{slot.SelectedSlot}}</span> <%=GetGlobalResourceObject("OPTBSRES","ExamTimeReminderMsg") %></p>
                  <div class="time-slot-row" v-for="hourly in slot.TimeSlot">
                      <div class="hourly-indicator"><span class="time">{{hourly.Hour}}</span></div>
                      <ul class="time-slots-list">
                            <li v-for="timeslot in hourly.Slots" class="time-slot" v-bind:class="{'selected': timeslot.Status=='selected', 'unavailable': timeslot.Status =='unavailable', 'active': timeslot.Status=='available'}" v-on:click="updateSelectedSlot(hourly,timeslot), timeslot.Status='selected'">
                              <span class="time">{{timeslot.Time}}</span>
                              <div class="selected-indicator"><span aria-hidden="true" class=" fa fa-check-circle" aria-hidden="true"></span></div>
                            </li>
                      </ul>
                  </div>
            </div>

###

You need to return data as a method, not just an object in order for it to be reactive.

var tslot = new Vue({
  el: '#time-slots-container',
  data() {
    return {
      slot: {}
    }
  },
  // etc

You can also use this.$http.get({}) as a promise which will remove your whole jquery mess.

Then you can use a fat arrow => to get a lexical this into your promise so you don’t have to pass around context (that)

If you do all that then your data should be reactive.