Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue 3: Wait until parent is done with data fetching to fetch child data and show loader

I'm looking for a reusable way to display a full page loader (Sidebar always visible but the loader should cover the content part of the page) till all necessary api fetches has been done.

I've got a parent component LaunchDetails wrapped in a PageLoader component

LaunchDetails.vue

<template>
  <PageLoader>
    <router-link :to="{ name: 'launches' }"> Back to launches </router-link>
    <h1>{{ name }}</h1>

    <section>
      <TabMenu :links="menuLinks" />
    </section>

    <section>
      <router-view />
    </section>
  </PageLoader>
</template>

<script>
import TabMenu from "@/components/general/TabMenu";

export default {
  data() {
    return {
      menuLinks: [
        { to: { name: "launchOverview" }, display_name: "Overview" },
        { to: { name: "launchRocket" }, display_name: "Rocket" },
      ],
    };
  },
  components: {
    TabMenu,
  },
  created() {
    this.$store.dispatch("launches/fetchLaunch", this.$route.params.launch_id);
  },
  computed: {
    name() {
      return this.$store.getters["launches/name"];
    },
  },
};
</script>

PageLoader.vue

<template>
  <Spinner v-if="isLoading" full size="medium" />
  <slot v-else></slot>
</template>

<script>
import Spinner from "@/components/general/Spinner.vue";

export default {
  components: {
    Spinner,
  },
  computed: {
    isLoading() {
      return this.$store.getters["loader/isLoading"];
    },
  },
};
</script>

The LaunchDetails template has another router-view. In these child pages new fetch requests are made based on data from the LaunchDetails requests.

RocketDetails.vue

<template>
  <PageLoader>
    <h2>Launch rocket details</h2>

    <RocketCard v-if="rocket" :rocket="rocket" />
  </PageLoader>
</template>

<script>
import LaunchService from "@/services/LaunchService";
import RocketCard from "@/components/rocket/RocketCard.vue";

export default {
  components: {
    RocketCard,
  },
  mounted() {
    this.loadRocket();
  },
  data() {
    return {
      rocket: null,
    };
  },
  methods: {
    async loadRocket() {
      const rocket_id = this.$store.getters["launches/getRocketId"];

      if (rocket_id) {
        const response = await LaunchService.getRocket(rocket_id);
        this.rocket = response.data;
      }
    },
  },
};
</script>

What I need is a way to fetch data in the parent component (LaunchDetails). If this data is stored in the vuex store, the child component (LaunchRocket) is getting the necessary store data and executes the fetch requests. While this is done I would like to have a full page loader or a full page loader while the parent component is loading and a loader containing the nested canvas.

At this point the vuex store is keeping track of an isLoading property, handled with axios interceptors.

All code is visible in this sandbox

(Note: In this example I could get the rocket_id from the url but this will not be the case in my project so I'm really looking for a way to get this data from the vuex store)

like image 743
Thore Avatar asked Dec 16 '21 09:12

Thore


1 Answers

Im introduce your savior Suspense, this feature has been added in vue v3 but still is an experimental feature. Basically how its work you create one suspense in parent component and you can show a loading when all component in any depth of your application is resolved. Note that your components should be an async component means that it should either lazily loaded or made your setup function (composition api) an async function so it will return an async component, with this way you can fetch you data in child component and in parent show a fallback if necessary.

More info: https://v3.vuejs.org/guide/migration/suspense.html#introduction

like image 123
TEFO Avatar answered Oct 06 '22 02:10

TEFO