Vue 3 - parent/child load mask

by skirtle

HTML

<script src="https://unpkg.com/[email protected]/dist/vue.global.js"></script>
<div id="app">
  <div v-for="i in 3" :key="i">
    <label><input type="radio" v-model="id" :value="i"> {{ i }}</label>
  </div>
  <child-component :id="id"></child-component>
  <div class="load-mask" v-show="$loading.isLoading.value"></div>
</div>

CSS

.load-mask {
  background: #ccc;
  bottom: 0;
  left: 0;
  opacity: 0.4;
  position: fixed;
  right: 0;
  top: 0;
  z-index: 100;
}

JavaScript

const ChildComponent = {
  props: ['id'],

  template: `
    <div>
      {{ id }}
      <template v-if="$loading.isLoading.value">
        - Loading
      </template>
      <button @click="loadData">Reload</button>
    </div>
  `,

  methods: {
    loadData () {
      this.$loading.start()

      // Pretend HTTP request
      clearTimeout(this.timer)

      this.timer = setTimeout(() => {
        this.$loading.stop()
      }, 2000)
    }
  },

  watch: {
    id: {
      flush: 'pre',
      
      handler () {
        this.loadData()
      }
    }
  }
}

const app = Vue.createApp({
  components: {
    ChildComponent
  },

  data () {
    return {
      id: 1
    }
  }
})

app.config.globalProperties.$loading = {
  isLoading: Vue.ref(false),

  start () {
    this.isLoading.value = true
  },

  stop () {
    this.isLoading.value = false
  }
}

app.mount('#app')