2

I have a simple application which need to render 2 components dynamically.

  • Component A - needs to have onClick event.
  • Component B - needs to have onChange event.

How is it possible to dynamically attach different events to component A/B?

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

<script>
import A from '../components/a.vue'
import B from '../components/b.vue'

export default {
  data: function () {
    return {
      currentView: A
    }
  },
  components: { A, B }
}
</script>

2 Answers 2

4

Here is a solution for a little more complicated and realistic use case. In this use case you have to render multiple different components using v-for.

The parent component passes an array of components to create-components. create-components will use v-for on this array, and display all those components with the correct event.

I'm using a custom directive custom-events to achieve this behavior.

parent:

<template>
    <div class="parent">
        <create-components :components="components"></create-components>
    </div>
</template>

<script>
import CreateComponents from '@/components/CreateComponents'
import ComponentA from '@/components/ComponentA'
import ComponentB from '@/components/ComponentB'

export default {
    name: 'parent',
    data() {
        return {
            components: [
                {
                    is: ComponentA,
                    events: {
                        "change":this.componentA_onChange.bind(this)
                    }
                },
                {
                    is: ComponentB,
                    events: {
                        "click":this.componentB_onClick.bind(this)
                    }
                }
            ]
        }
    },
    methods: {
        componentA_onChange() {
            alert('componentA_onChange');
        },
        componentB_onClick() {
            alert('componentB_onClick');
        }
    },
    components: { CreateComponents }
};
</script>

create-components:

<template>
    <div class="create-components">
        <div v-for="(component, componentIndex) in components">
            <component v-bind:is="component.is" v-custom-events="component.events"></component>
        </div>
    </div>
</template>

<script>
export default {
    name: 'create-components',
    props: {
        components: {
            type: Array
        }
    },
    directives: {
        CustomEvents: {
            bind: function (el, binding, vnode) {
                let allEvents = binding.value;
                if(typeof allEvents !== "undefined"){
                    let allEventsName = Object.keys(binding.value);
                    allEventsName.forEach(function(event) {
                        vnode.componentInstance.$on(event, (eventData) => {
                            allEvents[event](eventData);
                        });
                    });
                }
            },
            unbind: function (el, binding, vnode) {
                vnode.componentInstance.$off();
            }
        }
    }
  }
</script>
Sign up to request clarification or add additional context in comments.

Comments

1

You don't have to dynamically add them.

<component v-bind:is="currentView" @click="onClick" @change="onChange">

If you want to be careful you can bail in the handler of the currentView is not correct.

methods: {
  onClick(){
    if (this.currentView != A) return
    // handle click
  },
  onChange(){
    if (this.currentView != B) return
    // handle change
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.