Edgar Quintero
Edgar Quintero

Reputation: 4441

Prop value isn't updating in child component Vue.js

I'm adding a way for the parent App.vue tell all child components to close every modal you have open and for any given child to tell the parent to hide the overlay if someone clicks on the main app div. So for example if the services page has a modal open to view some details, an overlay will be active that is set in the parent App.vue component, and if I click anywhere outside of the modal, the modal will close and the child will tell the parent to close the overlay. I have it setup this way so that it's something globally available and not isolated to a single component.

<template>
  <div id="app" v-cloak :class="{'overlay-layer' : overlay.active}" v-on:click="close($event)">

    <Header/>

    <transition>
      <router-view v-on:overlay="overlay.active = $event" :closeAllModals="overlay.close"/>
    </transition>

    <Footer/>

  </div>
</template>

<script>
import Header from '@/components/header/Header.vue';
import Footer from '@/components/footer/Footer.vue';

export default {
  components: {
    Header,
    Footer
  },
  props: {
    closeAllModals: Boolean
  },
  data: function() {
    return {
      overlay: { active: false, close: false }
    };
  },
  methods: {
    close(e) {
      if (this.overlay.active && e.toElement.id === 'app') {
        this.overlay = {
          active: false,
          close: true
        }
      }
    }
  }
};
</script>

The problem is that this only executes once, so for example in the services page I have:

import Header from '@/components/header/Header.vue';
import Footer from '@/components/footer/Footer.vue';

export default {
  name: 'services',
  components: {
    Header,
    Footer
  },
  props: {
    closeAllModals: Boolean
  },
  data: function() {
    return {
      overlay: false,
      activeMember: Object,
    };
  },
  methods: {
    setActiveMember(member, open) {
      this.activeMember = member;
      if (open) {
        this.activeMember.active = true;
        this.overlay = true;
      } else {
        this.activeMember.active = false;
        this.overlay = false;
      }
      this.$emit('overlay', this.overlay);
      this.$forceUpdate();
    },
  watch: {
    closeAllModals: function() {
      this.activeMember.active = false;
      this.overlay = false;
      this.$forceUpdate();
      console.log('runs once');
    }
  }
};

So this works, but only works the first time. The prop only sends the updated value to the child only once. I've tried watching the prop in the child and using forceUpdate too but it isn't working. How do I make this run every single time?

Upvotes: 0

Views: 163

Answers (1)

Gowri
Gowri

Reputation: 394

A global event bus could help when components have to talk to each other across different parent/child levels. Below is a good write up on that:

https://alligator.io/vuejs/global-event-bus

Note: but be sure to use it only when necessary and remove the listeners in beforeDestroy life cycle of the component

Upvotes: 1

Related Questions