PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Sunday, October 16, 2022

[FIXED] How to get an SVG spinner to show up before a heavy UI update?

 October 16, 2022     css, javascript, svg, vue.js, vuejs3     No comments   

Issue

I have a UI update in a Vue app where I update 10 apexcharts bar charts at once. It takes about one second for it to fully load, during which time I'd like to show an svg spinner. However, the normal pattern of having a v-if="showSpinner" on a div containing that spinner and setting showSpinner.value = true; for a const showSpinner = ref(false); isn't working. For example:

<template>
    <div v-if="showSpinner">
       [svg spinner]
    </div>
    <div>
       [10 apexcharts I'm updating at once with already existing local data]
    </div>
</template>

<script setup lang="ts">

import { ref, nextTick } from 'vue';

const showSpinner = ref(false);
const state = reactive({
    dataForCharts: {},
});

const someFuncThatGetsCalledMuchLater = () => {
    showSpinner.value = true;

    // I've also tried await nextTick(); (after making this
    // function itself async) but that didn't make any difference.
    nextTick(() => {
        // some code that modifies state.dataForCharts, causing the charts to redraw    
    });
}

</script>

Ignoring even the code that would have to exist to set showSpinner.value = false; later on, which is another problem I've yet to solve, this above code doesn't show the spinner until after all of the charts have updated (which basically freezes the webpage for 1s, since my understanding is that Javascript is single-threaded).

So my two questions are:

  1. How can I get this svg spinner to appear before the charts start updating and freeze the webpage?
  2. Not here yet since I haven't solved (1), but how can I listen for a callback for when the charts have finished updating? I don't think directives will work for me since it seems that they are supposed to only act on the element that they're attached to, and I don't think setting showSpinner.value = false; inside onUpdated(...) would work either since that is called after every reactive update, including the changing of showSpinner.value = true;, so setting it false there would just instantly undo the fact that we've just set it to true.

Solution

1. show spinner before charts

Basically you just want to update showSpinner first, then trigger a redraw, and then render the charts. The simplest way would be to use nextTick

import { ref, nextTick } from 'vue';

const someFuncThatGetsCalled = async () => {
  showSpinner.value = true;
  await nextTick(); // could also pass a callback
  state.dataForCharts = someDataAndNotNullAnyMore;
}

nextTick, according to documentation, should work:

A utility for waiting for the next DOM update flush.

Details

When you mutate reactive state in Vue, the resulting DOM updates are not applied synchronously. Instead, Vue buffers them until the "next tick" to ensure that each component updates only once no matter how many state changes you have made.

nextTick() can be used immediately after a state change to wait for the DOM updates to complete. You can either pass a callback as an argument, or await the returned Promise.

However even though the DOM is updated, the browser may not re-render it. The subsequent js may block the rendering essentially rendering nextTick() useless. To deal with this issue you need to manually defer initiating the js-heavy portion until the dom renders the initial change. You can use setTimeout or requestAnimationFrame to accomplish that. Using setTimeout may work in some situations but is reported to behave inconsistently between browsers. It also can succumb similar issue as nextTick and you may need to use a higher timeout than 0 to accommodate. using nested requestAnimationFrame calls will wait for the browser to make the two renders before continuing, so, theoretically, is more reliable.

async function nextTwik(){
  return new Promise((resolve)=>{
    requestAnimationFrame(()=>{
      requestAnimationFrame(()=>{
        resolve()
      })
    })
  })
}

then you can use as:

const someFuncThatGetsCalled = async () => {
  showSpinner.value = true;
  await nextTwik();
  state.dataForCharts = someDataAndNotNullAnyMore;
};

2. How to tell if a chart got mounted

apexcharts allow passing a configuration option where you can define the events

docs

const chartsMounted = ref(0);

const chartOptions = {
  chart: {
    id: "bar-shart",
    events: {
      mounted(){
        chartsMounted.value ++;
      }
    },
    // animations, etc...
  },
  // dataLabels, plotOptions, xaxis, etc...
}

const someFuncThatGetsCalled = async () => {
  showSpinner.value = true;
  await nextTwik(); // defined above
  // kick off chart rendering
  state.dataForCharts = someData;
}

watch(chartsMounted, (num) => {
  if (num === 10) {
    // all 10 charts loaded
    console.log("👍great success👍");
    showSpinner.value = false
  }
})

As long as all charts have the mounted event listener that updates chartsMounted this would notify you when all charts are loaded.

Update

The chart event functionality was tested with Vue2 and not Vue3. ATTOW, there is a bug in the vue3 version where this doesn't work. According to the responses, apexcharts support adding the event listeners to the component using @, for example @mounted. I'm leaving the options.events based solution too since it reflects to the documentation.

const chartsMounted = ref(0);

function onChartMounted(){
  chartsMounted.value ++;
}

const someFuncThatGetsCalled = async () => {
  showSpinner.value = true;
  await nextTwik();
  // kick off chart rendering
  state.dataForCharts = someData;
}

watch(chartsMounted, (num) => {
  if (num === 10) {
    // all 10 charts loaded
    console.log("👍great success👍");
    showSpinner.value = false
  }
})

template:

<apex-chart :options="options" :series="series" @mounted="onChartMounted" />


Answered By - Daniel
Answer Checked By - Cary Denson (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing