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

2

6

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?

Etch answered 2/3, 2017 at 18:15 Comment(0)
H
3

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.

Hosea answered 3/3, 2017 at 17:36 Comment(1)
You are right. When mutating, the time continues to increment with the other answer. ThanksEtch
M
2

Vue.js will actually let you bind prop values to methods on the parent's scope, so the easiest way will be to do something like this:

<agenda class="agenda" v-for="(agenda,index) in event.agendas"
    :key="agenda.id"
    :index="index"
    :agenda="agenda"
    :start-time="get_start_time(agenda.duration)">
</agenda>

And then the get_start_time() is a method within the parent's scope:

new Vue({
  el: '#app',
  data: {
    time_of_day: '',
    event:{
      // ...
    },
  },

  methods: {
    get_start_time(duration) {
      if (this.next_event_start === '') {
        this.next_event_start = moment(this.event.start_date);
      } 
      this.event_start = this.next_event_start.format('h:mm a');
      this.next_event_start.add(duration, 'minutes');
      return this.event_start;
    },
  },
});

I've made a quick CodePen as a basic example to show it in action, but you'll need to update the actual code to compensate for multiple days.

Mobley answered 3/3, 2017 at 1:11 Comment(3)
Nice answer ... however, when I looked at the codepen it shows the agendas starting at 3:10 even though the event starts at 2. It seems like the get_start_time() method gets called more than once per agenda. Also, do you know if this will work if the agendas array is mutated? It seems like if you modify the agendas (add, remove, reverse), the start times will be re-calculated starting with the already incremented time_of_day value.Hosea
@Hosea I think that might've resulted from re-applying the Moment library to itself on each iteration. I've fixed up the codepen to work properly now.Mobley
As for your question about the re-calculation, all you would need to do, based on the revisions I just did, would be simply to set next_event_start back to an empty string (next_event_start = '') when you're modifying your agenda values. It should all re-calculate itself properly on the next tick of the Vue component update cycle.Mobley

© 2022 - 2024 — McMap. All rights reserved.