Skip to content
Advertisement

Skeleton loader component

I’m new to VueJS, and I’m coming to you to find out if what I’ve done is feasible or not.

Instead of having old data, while loading components, I prefer to display a preloader. I liked the idea of a skeletons loader, rather than a simple spinner.

Right now I have a state in the store, which is null by default, I have a mutation to set the loading, and a getter. To avoid visual bugs, from the router, with a beforeEach, I initialize the loading state to true, so that by default the components start loading !

Then, in my view, I import the Loader component, with its svg and style. And I place it over the component that needs to be displayed, with a simple condition v-if=”!getLoading” and v-if=”getLoading”.

The problem is that I feel like I’m tinkering with the blind, the beforeach and displaying this component with a condition?

I would be reassured if someone can give me some advice, or approve this method of doing!

Here is the code of a simple Loader component

<template>
    <content-loader
        :height="78"
        :width="390"
        :speed="4"
        primaryColor="#f2f6fe"
        secondaryColor="#e0eafa"
    >
        <rect x="9" y="20" rx="4" ry="4" width="142" height="13" />
        <rect x="316.38" y="5.38" rx="5" ry="5" width="68" height="68" />
        <rect x="9" y="46" rx="4" ry="4" width="75.26" height="13.26" />
    </content-loader>
</template>

<script>
import { ContentLoader } from "vue-content-loader"

export default {
    components: {
        ContentLoader
    }
}
</script>

The store code

const state = {
    loading: null,
}

const mutations = {
    SET_LOADING: (state, payload) => {
        state.loading = payload;
    },
}

const getters = {
    getLoading(state) {
        return state.loading;
    }
}

Example of utilisation in my view : with condition

<div class="col-12 col-lg-4 col-xl-3 col-md-6" v-if="getLoading"> // the condition
                        <div class="card animate-up-2">
                            <div class="card-body">
                            // the component
                                <StatsLoader></StatsLoader>
                            </div>
                        </div>
                    </div>
                    <div class="col-12 col-lg-4 col-xl-3 col-md-6" v-if="!getLoading"> // the condition
                        <div class="card animate-up-2">
                            <div class="card-body">
                                <div class="d-flex align-items-center justify-content-between">
                                    <div>
                                        <h3 class="font-weight-bold text-uppercase">5 %</h3>
                                        <div class="d-flex d-sm-block d-lg-flex align-items-end">
                                            <p class="mb-0 mr-2 text-muted">API usage</p>
                                        </div>
                                    </div>
                                    <div>
                                        <div class="avatar avatar-lg">
                                            <div class="avatar-title sred sbg-soft-red rounded">
                                                <i class="fad fa-project-diagram"></i>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

Advertisement

Answer

If that is a global loader being used on all the routes, you can wrap it in a component and use that component everywhere. Use named slots to manage your template. An example of a Loader component:

Loader.vue

<template>
   <div>
     <slot name='loading' v-if='getLoading'>
        <StatsLoader></StatsLoader>
     </slot>
     <slot name='content v-if='!getLoading'></slot>
   </div>
</template>
<script>
  import StatsLoader from '@/components/StatsLoader.vue';
  export default {
    name: 'Loader',
    props: {
      isLoading: {
        type: Boolean,
        default: null
      }
    },
    computed: {
      getLoading() {
        return this.isLoading === null ? this.$store.state.loading : this.isLoading;
      }
    },
    components: {
      StatsLoader
    }
  }
</script>

Also, it would useful to register this component globally so you don’t need to include it all the routes. You can do that using Vue.component in your main entry file.

import Loader from '@/components/Loader.vue';

Vue.component('Loader', Loader);

And you can rewrite your current component template like this:

<Loader>
  <template v-slot:loader>
    <!-- Use a different loader here for this page maybe -->
  </template>
  <template v-slot:content>
    <div class="card animate-up-2">
      <div class="card-body">
         <div class="d-flex align-items-center justify-content-between">
            <div>
               <h3 class="font-weight-bold text-uppercase">5 %</h3>
               <div class="d-flex d-sm-block d-lg-flex align-items-end">
                  <p class="mb-0 mr-2 text-muted">API usage</p>
               </div>
            </div>
            <div>
               <div class="avatar avatar-lg">
                  <div class="avatar-title sred sbg-soft-red rounded">
                     <i class="fad fa-project-diagram"></i>
                  </div>
               </div>
            </div>
         </div>
      </div>
   </div>
  </template>
</Loader>

Also you can use <Loader :is-loading='isPageLoading' /> in case you don’t want to rely on the global loader.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement