Jaroslav Klimčík
Jaroslav Klimčík

Reputation: 4808

Vue.js - multiple event with dynamic components

I have a simple root App which includes two different components Room and Machine. And each of these components include one component Datatable which is totally same for both of them. To switch between Room and Machine I'm using dynamic components mechanism, nothing special. When component is changing then I just emit event and component Datatable should log it in console with current module name. The problem is that each time I change component, event is sent multi times. If I understand right, after changing component the old one should be destroyed and the new one created so why this is happening? I'm using Vue.js v.2.4.1

Here is screenshot from console when I switch between components: enter image description here

Here are components:

App.vue:

<template>
    <div id="app">
        <select style="padding: 10px" v-model="currentModule" @change="changeComponent">
            <option value="Room">Room</option>
            <option value="Machine">Machine</option>
        </select>
        <component :is="currentModule"></component>
    </div>
</template>

<script>
    import Room from './Room.vue';
    import Machine from './Machine.vue';


    export default {
        name: 'app',
        components: {
            Room,
            Machine
        },
        data () {
            return {
                currentModule: 'Room'
            }
        },
        methods: {
            changeComponent: function() {
                Event.$emit('moduleHasChanged', this.currentModule)
            }
        },
    }
</script>

Machine.vue:

<template>
    <div>
        Machine template

        <datatable></datatable>
    </div>
</template>
<script>
    import Datatable from './Datatable.vue';

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

Room.vue:

<template>
    <div>
        Room template

        <datatable></datatable>
    </div>
</template>
<script>
    import Datatable from './Datatable.vue';

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

Datatable.vue

<template>
    <div>
        Datatable
    </div>
</template>

<script>
    export default {
        created() {
            Event.$on('moduleHasChanged', (currentModule) => {
                console.log(currentModule);
            })
        }
    }
</script>

Upvotes: 1

Views: 2684

Answers (1)

Bert
Bert

Reputation: 82459

This is happening because you are continually adding event listeners (when the Datatable component is created) and never removing them. Vue typically handles this for you, but since you are using an event bus you need to do it yourself.

Simply add a beforeDestroy handler and remove the event handler.

console.clear()
const Event = new Vue()

const Datatable = {
  template: `
    <div>
      Datatable
    </div>
  `,
  methods: {
    moduleChanged(currentModule) {
      console.log(currentModule);
    }
  },
  created() {
    Event.$on('moduleHasChanged', this.moduleChanged)
  },
  beforeDestroy() {
    Event.$off('moduleHasChanged', this.moduleChanged)
  }
}

const Room = {
  template: `
    <div>
      Room template
      <datatable></datatable>
    </div>
  `,
  components: {
    Datatable
  }
}

const Machine = {
  template: `
    <div>
      Machine template
      <datatable></datatable>
    </div>
  `,
  components: {
    Datatable
  }
}

const App = {
  name: 'app',
  template: `
    <div id="app">
        <select style="padding: 10px" v-model="currentModule" @change="changeComponent">
            <option value="Room">Room</option>
            <option value="Machine">Machine</option>
        </select>
        <component :is="currentModule"></component>
    </div>
  `,
  components: {
    Room,
    Machine
  },
  data() {
    return {
      currentModule: 'Room'
    }
  },
  methods: {
    changeComponent: function() {
      Event.$emit('moduleHasChanged', this.currentModule)
    }
  },
}


new Vue({
  el: "#app",
  render: h => h(App)
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">

</div>

Upvotes: 1

Related Questions