Vue.js v-for increment some variable in looped component

BassMHL picture BassMHL · Mar 2, 2017 · Viewed 17.3k times · Source

I'm trying to display a list of items on an event's agenda.

The event has a start_date end each item on the agenda has a duration in minutes, for example:

event:{
  start_date: '2017-03-01 14:00:00',
  agendas:[
      {id:1,duration:10},
      {id:2,duration:15},
      {id:3,duration:25},
      {id:4,duration:10}
  ]
}

Now, in my event component, I load agendas with a v-for:

<agenda v-for="(agenda,index) in event.agendas" 
        :key="agenda.id"
        :index="index" 
        :agenda="agenda">

In agenda component, I want to increment the time at which each item starts:

<div class="agenda">
  //adding minutes to start_date with momentJs library
  {{ moment(event.start_date).add(agenda.duration,'m') }} //this should increment, not add to the fixed event start date
</div>

Currently it only adds to the fixed event start_date... I would like to show the times 14:00 for event 1, 14:10 for event 2, 14:25 for event 3 and 14:50 for event 4.

How can I increment the value in a v-for directive in Vue.js 2.0?

Answer

Peter picture Peter · Mar 3, 2017

It looks like you already got an answer that works for you but I'll post this here in case anyone is looking for an alternate solution. The accepted answer might be good for an initial render of the agendas but will start to break if the agendas array is mutated or anything causes the list to re-render because the start time calculation is based on a stored value that gets incremented every iteration.

The code below adds a computed property (based on event.agendas) that returns a new array of of agenda objects, each with an added start property.

Vue.component('agenda', {
  template: '<div>Agenda {{ agenda.id }} — {{ agenda.duration }} min — {{ agenda.start }}</div>',
  props: {
    agenda: Object
  }
});

new Vue({
  el: "#root",
  data: {...}, 
  computed: {
    agendas_with_start() {
      const result = [];

      let start = moment(this.event.start_date);
      for(let agenda of this.event.agendas) {
        result.push({
          id: agenda.id,
          duration: agenda.duration,
          start: start.format('HH:mm')
        });
        start = start.add(agenda.duration, 'm');
      }
      return result;
    }
  }
});

Then, in the template, the agendas_with_start computed property is used in the v-for:

<div id="root">
  <h3>Event Start: {{ event.start_date }}</h3>
  <agenda v-for="agenda in agendas_with_start" 
          :key="agenda.id"
          :agenda="agenda"></agenda>
</div>

Here's a working codepen. The benefit of this approach is that if the underlying agendas array is mutated or re-ordered or the event start time changes or anything causes Vue to re-render the DOM, this computed property will be re-evaluated and the start times will be re-calculated correctly.