0

I'm experiencing a problem where a custom event (swap-components) emitted from a dynamic component (A.vue or B.vue) is not being listened to correctly in the parent of the dynamic component (HelloWorld.vue).

Here is the source on GitHub (created using vue cli 3).

Here is a live demo showing the problem.

In the live demo, you'll see that clicking the button in the dynamic component with background color DOES NOT change the dynamic component. But when clicking the button below the background color (which originates in the HelloWorld.vue parent), the dynamic component DOES INDEED change.

Why is this happening and how to fix it?


Below I'll copy over the contents of the 3 main files of interest into this post:

  1. HelloWorld.vue (the parent)

  2. A.vue (sub component used in dynamic component)

  3. B.vue (sub component used in dynamic component)

HelloWorld.vue:

<template>
  <div>
    <h1>The dynamic components ⤵️</h1>
    <component
      :is="current"
      v-bind="dynamicProps"
    ></component>
    <button
      @click="click"
    >Click me to swap components from within the parent of the dynamic component</button>
  </div>
</template>

<script>
import A from "./A.vue";
import B from "./B.vue";

export default {
  data() {
    return {
      current: "A"
    };
  },
  computed: {
    dynamicProps() {
      return this.current === "A" ? { data: 11 } : { color: "black" };
    }
  },
  methods: {
    click() {
      this.$emit("swap-components");
    },
    swapComponents() {
      this.current = this.current === "A" ? "B" : "A";
    }
  },
  mounted() {
    this.$nextTick(() => {
      // Code that will run only after the
      // entire view has been rendered
      this.$on("swap-components", this.swapComponents);
    });
  },
  components: {
    A,
    B
  },
  props: {
    msg: String
  }
};
</script>

A.vue:

<template>
  <section id="A">
    <h1>Component A</h1>
    <p>Data prop sent from parent: "{{ data }}"</p>
    <button @click="click">Click me to swap components from within the dynamic component</button>
  </section>
</template>

<script>
export default {
  props: ["data"],
  methods: {
    click() {
      this.$emit("swap-components");
    }
  }
};
</script>

B.vue:

<template>
  <section id="B">
    <h1>Component B</h1>
    <p>Color prop sent from parent: "{{ color }}"</p>
    <button @click="click">Click me to swap components from within the dynamic component</button>
  </section>
</template>

<script>
export default {
  props: ["color"],
  methods: {
    click() {
      this.$emit("swap-components");
    }
  }
};
</script>
Brian Zelip
  • 2,909
  • 4
  • 33
  • 45

2 Answers2

2

I'm guessing this is because the event listener is listening for a swap-components event emitted by the parent component itself. Perhaps you can fix that by listening for a swap-components event from the child component then emitting an event on the parent component.

<template>
  <div>
    <h1>The dynamic components ⤵️</h1>
    <component
      :is="current"
      v-bind="dynamicProps"
      @swap-components="$emit('swap-components')"
    ></component>
    <button
      @click="click"
    >Click me to swap components from within the parent of the dynamic component</button>
  </div>
</template>

Or you can call your method directly when the event is emitted by the child component ..

<template>
      <div>
        <h1>The dynamic components ⤵️</h1>
        <component
          :is="current"
          v-bind="dynamicProps"
          @swap-components="swapComponents"
        ></component>
        <button
          @click="click"
        >Click me to swap components from within the parent of the dynamic component</button>
      </div>
    </template>
sepehr
  • 17,110
  • 7
  • 81
  • 119
Husam Ibrahim
  • 6,999
  • 3
  • 16
  • 28
1

this is not bound to the context anymore when you use function. It is only limited to the function scope. Use arrow function to let this bound to the parent context.

Change:

this.$nextTick(function() {

With:

this.$nextTick(() => {
Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231
  • The arrow fn still doesn't work - i updated my code above and the live demo. – Brian Zelip Dec 29 '18 at 03:23
  • 1
    The event emitted from child component should work fine. The problem I see you're using emit on parent component. You probably want to use on? This [post](https://stackoverflow.com/questions/49387868/vue-js-emit-is-not-working/49387920#49387920) may help you. – Bhojendra Rauniyar Dec 29 '18 at 03:26