Search code examples
vue.jsvuejs3quasar

Vue 3 Dom not updating during function call


I have a simple function that is flipping some variables. (placeholder for the real logic). the issue is that Vue is not updating the dom when a variable is updated until the end of the function. I have tried nextTrick() with no success.

part of the template:

<div>
  <q-icon v-if="status === 'DONE'" name="mdi-check-circle" color="green" size="2em"/>
  <q-icon v-if="status === 'PENDING'" name="mdi-checkbox-blank-circle-outline" color="orange-8" size="2em"/>  
  <q-icon v-if="status === 'FAILED'" name="mdi-alert-circle" color="red-8" class="q-pr-sm" size="2em"/> 
  <q-spinner-pie  v-if="status === 'PROCESSING'" color="primary" size="2em" class="q-pr-sm" /> 
  <span class="text-italic text-caption" >( {{ curTotal }} / {{ itemTotal }} )</span>

</div>

My very simple function:

        processImages(){  
            this.status = 'PROCESSING'
            for ( let i = 0; i < this.itemTotal; i++){
                this.sleep(1000)
                this.curTotal += 1
            }
        }

Edit

I changed my function to call my api endpoint instead of the sleep() function.

        async processImages(){  
            this.rows[0].status = 'PROCESSING'
            for ( let i = 0; i < this.itemTotal; i++){
                const { data } = await $fetch('/api/scrape', {
                    method: 'GET',
                })
                this.rows[0].curTotal += 1
            }
            this.rows[0].created = true
        }

The sleep function i was using was:

        sleep(miliseconds) {
            var currentTime = new Date().getTime();

            while (currentTime + miliseconds >= new Date().getTime()) {
            }
        }

It was the sleep function that was blocking the ui update.


Solution

  • It appears to work just fine, maybe provide some more code like your sleep method:

    <script>
    export default {
      data() {
        return {
          curTotal: 1,
          itemTotal: 20,
          status: "FAILED"
        }
      },
      methods: {
        async sleep(ms) { return new Promise((r) => setTimeout(r, ms)) },
        async processImages() {  
            this.status = 'PROCESSING'
              // do what ever
              for ( let i = 0; i < this.itemTotal/2; i++){
                  await this.sleep(1000)
              this.curTotal += 1
          }
          this.status = 'DONE'
        },
      },
    }
    </script>
    

    An alternative would be to switch to a composition api and using a ref for the status,itemTotal and curTotal

    vue-ref

    <script setup>
    import { ref } from 'vue'
    
    const status = ref('FAILED')
    const curTotal = ref(0)
    const itemTotal = ref(20)
    const sleep = ms => new Promise((r) => setTimeout(r, ms));
    async function processImages() {  
      status.value = 'PROCESSING'
      // do what ever
      for ( let i = 0; i < itemTotal.value; i++){
         await sleep(1000)
         curTotal.value += 1
      }
      status.value = 'DONE'
    }
    </script>
    
    <template>
      <div>
        <span v-if="status === 'FAILED'">FAILED</span>
        <span v-if="status === 'PROCESSING'">PROCESSING</span>
        <span v-if="status === 'DONE'">DONE</span>
        <button @click=processImages>
         Trigger
        </button>
        <span class="text-italic text-caption" >( {{ curTotal }} / {{ itemTotal }} )</span>
    
      </div>
    </template>