Search code examples
vue.jssettimeoutcounter

Waiting time counter for each object in array Vue js


To get in context, I have a table that shows incoming calls and the waiting time for every call. The data array looks like:

[
   {
      id: 1,
      patient_name: lorem ipsum,
      created_at: 2022-02-02 09:10:35,
      ...
   },
   {
      id: 2,
      patient_name: dolor ipsum,
      created_at: 2022-02-02 09:00:35,
      ...
   }
]

I'm trying to figure out how to assign a setTimeout for each object, but I'm completely lost.

So far, I found that a counter can be made through a watcher, but of course this only acts as a "global" counter.

watch: {
        timerCount: {
            handler (value) {
                if (value > 0) {
                    setTimeout(() => {
                        this.timerCount++
                    }, 1000)
                }
            },
            immediate: true // This ensures the watcher is triggered upon creation
        }
    },

Is there a way to use a function to show a counter on each object? I was thinking in something like this:

<template>
    <span v-for="call in calls" :key="call.id">
       Requested at: {{ call.created_at }}
       waiting time: {{ showWaitingTime(call.created_at) }} // <- Not sure if this can be done
    </span>
</template>
...
<script>
    ....
    methods: {
        showWaitingTime (created_at) {
            // get diff in seconds between created_at and now
            // do something here with a timeOut() to increment the object property...
        }
    }
</script>

In addition, I would like to return the waiting time in HH:mm:ss format.


Solution

  • One solution is to wrap the {{ showWaitingTime(call.created_at) }} with a <span> that is keyed on timerCount, so that the <span> is re-rendered when timerCount changes (thus calling showWaitingTime again to compute the new time string):

    1. In the template, wrap the timestamp string with a <span> that has its key bound to timerCount:

      waiting time: <span :key="timerCount">{{ showWaitingTime(call.created_at) }}</span>
      
    2. In a watcher on calls, use setInterval to start a periodic timer. Be sure to stop the timer with clearInterval before starting a new timer and when unmounting the component.

      export default {
        beforeUnmount() {
          clearInterval(this._updateTimer)
        },
        // Vue 2 equivalent of beforeUnmount()
        beforeDestroy() {
          clearInterval(this._updateTimer)
        },
        watch: {
          calls: {
            handler(calls) {
              clearInterval(this._updateTimer)
              if (calls.length) {
                this._updateTimer = setInterval(() => this.timerCount++, 1000)
              }
            },
            immediate: true,
          },
        },
      }
      
    3. The watcher you have on timerCount is effectively implementing setInterval. Remove that code since it's obviated by the code in step 2.

      export default {
        watch: {
          // timerCount: {⋯}  // ⛔️ remove this watch
        }
      }
      
    4. In showWaitingTime(), calculate the HH:mm:ss from the difference between the given time and now:

      export default {
        methods: {
          showWaitingTime(created_at) {
            const diff = new Date() - new Date(created_at)
            const twoD = x => `${Math.floor(x)}`.padStart(2, '0')
            const HH = twoD((diff / (60 * 60 * 1000)) % 24)
            const mm = twoD((diff / (60 * 1000)) % 60)
            const ss = twoD((diff / 1000) % 60)
            return `${HH}:${mm}:${ss}`
          },
        },
      }
      

    demo