Search code examples
typescriptvue.jsvuejs3vue-composition-apieventemitter

How to emit to show/hide loader overlay?


I'm making a loader component for when my API calls are loading in. I'd like it to be a container I can place around anything. But I am very new to emits and am not quite sure how to use it in this scenario. What am I doing wrong?

This is my component AppLoader.vue:

<script setup lang="ts">
let loading = ref(false);

const showLoader = () => {
  loading.value = !loading;
};
</script>

<template>
  <div
    v-if="loading"
    :class="loading ? 'loading' : ''"
    class="loader f-height f-width"
  >
    LOADER WHOOOOOO
  </div>
  <slot></slot>
</template>

<style src="./app-loader.css" scoped lang="postcss" />

app-loader.css:

.loader {
  background: red;
  width: 100%;
  height: 100%;
  position: absolute;
  display: none;
  top: 0;
  left: 0;
  z-index: 10;
  opacity: 0.5;
}

.loading {
    display: block;
}

On the page I want to use it:

<AppLoader>Some stuff that takes a while to load....</AppLoader>

And in the script of my page:

<script lang="ts" setup>
const emit= defineEmits(["showLoader"])

onMounted(() => {
  emit("showLoader", true); // Show the loader by adding a display:block class
  // Fetch all my API calls and process the information on the page
  emit("showLoader", false) // Hide the loader by removing the display:block class
});
</script>

Solution

  • Maybe you can try with prop that you send from parent component and watch it in loader:

    const { ref, onMounted, watch } = Vue
    const app = Vue.createApp({
      setup() {
        const isLoading = ref(true)
        onMounted(() => {
          setTimeout(() => isLoading.value = false, 5000)
        })
        return { isLoading }
      }
    })
    app.component('loader', {
      template: `
        <div
          v-if="loading"
          :class="loading ? 'loading' : ''"
          class="loader f-height f-width"
        >
          LOADER WHOOOOOO
        </div>
        <slot></slot>
      `,
      props: {
        load: {
          type: Boolean,
          default: true
        }
      },
      setup(props) {
        const loading = ref(props.load);
        watch(
          () => props.load,
          (newValue) => loading.value = newValue
        );
        return { loading }
      }
    })
    app.mount('#demo')
    .loader {
      background: red;
      width: 100%;
      height: 100%;
      position: absolute;
      display: none;
      top: 0;
      left: 0;
      z-index: 10;
      opacity: 0.5;
    }
    .loading {
      display: block;
    }
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <div id="demo">
      <loader :load="isLoading">Some stuff that takes a while to load....</loader>
    </div>