Léo Coco
Léo Coco

Reputation: 4282

Vue.js - Dynamic component and events

I'm wondering how to deal with dynamic components and events using vue-js 2.3.

Let's say the component home $emit('eventFromHome') and the component archives $emit('eventFromArchives')

What is the way to catch them in the <component></component> ? Does it exist a better/nicer way to do it than the temporary solution?

Dynamic component

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})

<component v-bind:is="currentView"></component>

Static component

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})

<home v-on:eventFromHome=""></home>
<archive v-on:eventFromArchive=""></archive>

Temporary answer

<component 
    v-bind:is="currentView"
    v-on:eventFromHome=""
    v-on:eventFromArchive="">
</component>

Upvotes: 2

Views: 4501

Answers (3)

bernie
bernie

Reputation: 10390

Since Vue.js 2.4, v-on has an object syntax that allows dynamic event bindings.

With that, the solution is much more elegant:

var vm = new Vue({
  el: '#example',
  data: {
    currentView: {
      component: 'home',
      events: {}, // <-- put listeners here
    }
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})

<component v-bind:is="currentView.component" v-on="currentView.events"></component>

Upvotes: 5

iraj jelodari
iraj jelodari

Reputation: 3356

In my opinion the best practice is some thing like following approach. the idea is building a directive that attaches some listeners and handlers to dynamic components. thanks Javier Lecuona for his idea:

<template>
  <div>
    <component :is="component" v-bind="dynamicProps" v-dynamic-events="knownEvents"></component>
  </div>
</template>
<script>
import CoolComponent from './cool-component.vue';

export default {
  directives: {
    DynamicEvents: {
      bind: function (el, binding, vnode) {
        const allEvents = binding.value;
        allEvents.forEach((event) => {
          // register handler in the dynamic component
          vnode.componentInstance.$on(event, (eventData) => {
            // when the event is fired, the proxyEvent function is going to be called
            vnode.context.proxyEvent(event, eventData);
          });
        });
      },
      unbind: function (el, binding, vnode) {
        vnode.componentInstance.$off();
      },
    },
  },
  data() {
    return {
      component: CoolComponent,
      dynamicProps: {
        isThisCool: true,
      },
      knownEvents: ['event-1', 'event-2']
    };
  },
  methods: {
    proxyEvent(eventName, eventData) {
      // called by the custom directive
      // do whatever you please with the event :)
    }
  }
};

</script>

Upvotes: 2

thanksd
thanksd

Reputation: 55644

You can just add both @eventFromHome and @eventFromArchives to the dynamic component:

<component 
  :is="currentView"
  @eventFromHome="eventHandlerFoo" 
  @eventFromArchive="eventHandlerBar"
></component>

Upvotes: 2

Related Questions