methodsvue.jssettimeoutreminders

VueJS Reminder App and Update data array items independently


This is my second question related to VueJS. I will divide my question in two parts.

1. Why VueJS methods are keep getting called or running in loop? I thought startTimer() will be called once on load. Whenever I push new reminder in the array but that's keep getting called and freezes the browser. I think I don't understand methods in VueJS can anyone elaborate?

2. How do I update timer/counter for individual list items in data array (items)? To keep the track of timer and counter, I created elements in data array (items) as you can see in the codepen.

Codepen

<div id="reminders">
    <form v-on:submit.prevent="addReminder">
        <label for="new-reminder">Reminder</label>
        <input v-model="newReminder" type="text" id="new-reminder" placeholder="Feed the cat">
        <label for="new-timer">Time</label>
        <input v-model="newTimer" type="number" id="new-timer" placeholder="Time in minute"> 
        <button>Add</button>
    </form>
    <ul>
        <li v-for="item,index in items" class="reminder" v-bind:id='"reminder_"+item.id' v-bind:load="startTimer(index)"> 
            {{ item.id }}<span class="title">{{ item.title }}</span>
            TIMER: <span class="time">{{ item.time }}</span><br>
            COUNTER:<span class="counter">{{ item.counter }}</span><br>
            <button class="close" v-on:click="removeReminder(index)"><img src="https://image.flaticon.com/icons/svg/53/53804.svg" alt=""></button>
        </li>
    </ul>
</div> 

var reminder = new Vue({
    el: '#reminders',
    data: {
        newReminder: '',
        newTimer: 0,
        items: []
    },
    methods: {
        addReminder: function() {
            this.items.push({
                id: this.items.length,
                title: this.newReminder,
                time: this.newTimer,
                counter:0
            })
        },
        removeReminder: function(index) {
            this.items.splice(index, 1);
        },
        startTimer: function(index) {  
          if(this.items[index].counter<100)this.items[index].counter++; // if I don't do this. browser will freez and crash after sometime
          console.log(this.items[index].counter);
        }
    }
});

Alternative Code with Timer & Counter -

var reminder = new Vue({
    el: '#reminders',
    data: {
        newReminder: '',
        newTimer: 0,
        items: []
    },
    methods: {
        addReminder: function() {
            this.items.push({
                id: this.items.length,
                title: this.newReminder,
                time: this.newTimer,
                counter:0
            })
        },
        removeReminder: function(index) {
            this.items.splice(index, 1);
        },
        startTimer: function(index) {
            let item = this.items[index];
            let t = 0; 
            function timeout() {
                setTimeout(function() {
                    if (t == item.time * 60) {
                        $('#' + index).find('.title').css('text-decoration', 'line-through');
                    } else {
                        timeout();
                        item.counter = t++;
                        $('#' + index).find('.counter').text(item.time * 60 - item.counter);
                    }
                }, 1000);
            }
            timeout();
        }
    }
}); 

Solution

    1. I don't think we can use v-bind:load to trigger when the element is mounted to the dom. You can fix by removing the v-bind:load, but trigger the this.startTimer(this.items.length - 1) inside the addTimer method.

    2. You should you Vue.set(this.items, index, counter++);

    https://v2.vuejs.org/v2/api/#Vue-set