Home > Software design >  Vue router isLoading show loader div in App.vue
Vue router isLoading show loader div in App.vue

Time:12-29

I have a simple problem.

I'm using Vue3 with vite, typescript and router, and I can't figure out, how to trigger a loading screen whilst the user clicks on router paths.

I successfully understood and implemented that within the router/index.ts, I can call a router.beforeEach, in which I can toggle a variable. Called it isLoading, and put a timeout delay before another route is rendered, it looks as follows:

var isLoading = false

router.beforeEach((to, from, next) =>{
  console.log("beforeach");
  isLoading = true
  console.log(isLoading)

setTimeout(() => {
  next();
}, 2000);


});
router.afterEach((to, from) => {
  console.log("aftereach");
  var isLoading = false
  console.log(isLoading)

});

In my App.vue, I have a Preloader component imported from my views folder, which contains a simple div with some styling, and I want this component to get a class on beforeEach, and that class to be gone on afterEach.

But I have no idea, how to acquire the state of isLoading from my router/index.ts, or, if there is another way purely within App.vue that could trigger a class to my PreLoader component.

Thanks for your help!

Tried to use beforeEach and afterEach with a delay within router/index.ts, but don't know how to export the state of isLoading to App.vue.

CodePudding user response:

The following two approaches can help you-

1. Using event bus-

  • Emit an event on the router's before and after hook and pass a status true/false.
router.beforeEach((to, from, next) =>{
  eventbus.emit('loader-status', true)
});
router.afterEach((to, from) => {
  eventbus.emit('loader-status', true)
});
  • Listen to this event in your loader component and show/hide the loader as per the status passed by the event.
eventbus.on('loader-status', status => { this.showLoader = status })

Here you can read- How to use event buses in vue3.

2. Using Vuex-

  • Create a variable named showLoader in the Vuex state.
  • Update that variable on routers before and after the hook.
router.beforeEach((to, from, next) =>{
  // call a mutation to update the showLoader to true
});
router.afterEach((to, from) => {
  // call a mutation to update the showLoader to false
});
  1. Use this global variable's value in the Loader component to show/hide the loader.
<Loader v-if="store.state.showLoader"></Loader>

Here you can read- How to set up Vuex in vue3.

NOTE-
I only provided the logical code, syntax would be different when you use any of the above approaches.

CodePudding user response:

so thanks to Neha Soni's answer, I found out about

Pinia

and what I had in mind, looks like the following:

I installed pinia, created a store folder under src, and created there an isloading.ts file, which looks like this:

import { defineStore } from "pinia";
import { reactive, ref } from "vue";

export const  useLoaderState = defineStore("isLoading", () => {
    
    const state = ref(false)

const changeStateTrue = () => {
    state.value = true
}

const changeStateFalse = () => {
    state.value = false
}

    return { state, changeStateTrue, changeStateFalse }
})

In router/index.ts, I have the following logic that awaits for a bit before actually rendering the clicked route, and saves to the global variable isLoading as state a boolean:

router.beforeEach((to, from, next) => {
  const isLoading = useLoaderState();
  const { changeStateTrue } = isLoading;
  
  console.log("beforeach");
  changeStateTrue();
  console.log(isLoading.state);

  setTimeout(() => {
    next();
  }, 500);
});
router.afterEach((to, from) => {
  const isLoading = useLoaderState();
  const { changeStateFalse } = isLoading;

  console.log("aftereach");
  changeStateFalse();
  console.log(isLoading.state);
});

Finally, in my App.vue I have the following for my PreLoader, which is just for a loading screen:

 <script setup lang="ts">
   import { useLoaderState } from "./store/isloading";

   const isLoading = useLoaderState();
  </script>

  <template>
          <PreLoader v-if="isLoading.state" : />
  </template>

I wanted even my loader to change according to the already applied dark/light mode, so thats what the : is there for, what is important is the v-if="isLoading.state" , which first gets into this component with calling the Pinia way the isLoading global variable with

  • importing the defined store with import { useLoaderState } from "./store/isloading";
  • creating a constant in this current App.vue with the const isLoading = useLoaderState();

From there, the constant's values and states (including the boolean which I wanted) lives reactively in this current component (in this case, the App.vue), and the v-if will get updated therefore dynamically.

  • Related