Ryan Wennekes
Ryan Wennekes

Reputation: 89

What is the difference between Vue's 'v-on' directive and vue.$on?

Suppose I have two siblings components like so:

<div id="root2">
    <some-component>First</some-component>
    <some-component>Second</some-component>
</div>

... and these components are defined like so:

Vue.component('some-component', {
    template: '<button @click="doSomething" @somethingwasdone="this.reactToSomething" :disabled="this.isDisabled"><slot /></button>',
    methods: {
        doSomething() {
            Event.$emit('somethingWasDone');
        },

        reactToSomething() {
            this.isDisabled = true;
        }
    },
    data() {
        return {
            isDisabled: false,
        }
    },
    created() {
        Event.$on('somethingWasDone', () => this.reactToSomething());
    }
});

Suppose my aim is to disable both buttons if either one of them is pressed. I have purposely included two event-listeners, one in the form of a 'v-on' directive and one in the form of the $on method defined on a Vue instance. I cannot get the former to work and I am unsure as to why.

My guess is that it must have something to do with the scope of the emitted event. However, even the component on which I click does not not listen to the event it emits while using the v-on directive. Any help is greatly appreciated!

Regards,

Ryan


Update: I have found an error in my code, in which the event the v-for directive (@) was listening for was 'componentclicked' instead of 'somethingwasdone'. When fixing the error, I noticed that both methods in fact worked.

Upvotes: 0

Views: 861

Answers (2)

Igor Moraru
Igor Moraru

Reputation: 7729

Every Vue instance (including the root one) have $on and $emit methods, that allow emitting and receiving events. When dealing with events, you have to bear in mind the scope of the emitted event.

In your example, it seems you are trying to emit and catch an event in the same component. For this purpose you will want to use component instance $emit and $on methods. In this way the event is emitted in the component and remains in this scope. The syntax used in this case is this.$emit() and this.$on(), this defining the component instance.

Vue.component('some-component', {
    template: '<button @click="doSomething" @somethingWasDone="this.reactToSomething" :disabled="this.isDisabled"><slot /></button>',
    methods: {
        doSomething() {
            this.$emit('somethingWasDone');
        },

        reactToSomething() {
            this.isDisabled = true;
        }
    },
    data() {
        return {
            isDisabled: false,
        }
    },
    created() {
        this.$on('somethingWasDone', () => this.reactToSomething());
    }
});

v-on is the syntax used in template to listen to the same event, from the parent component.

<div id="root2">
     <some-component v-on:somethingWasDone="doSomething">First</some-component>
    <some-component v-on:somethingWasDone="doSomething">Second</some-component>
</div>

If you are using the root instance Vue.$on and Vue.$emit, your are emitting/listening to events at the global level.

Upvotes: 1

t-MURO
t-MURO

Reputation: 671

As I understand it, when you emit events they are emitted to the parent component. So you have to implement logic in the parent component.

<div id="root2">
  <some-component
    v-on:somethingWasDone="isDisabled = true"
    :disabled="isDisabled">
   First
  </some-component>

  <some-component
    v-on:somethingWasDone="isDisabled = true"
    :isDisabled="isDisabled">
   Second
  </some-component>
</div>

// ...

data() {
  return {
    isDisabled: false
  }
}

add the isDisabled variable to the parent and also as a prop in some-component.

Vue.component('some-component', {
    template: '<button @click="doSomething" @componentclicked="this.reactToSomething" :disabled="this.isDisabled"><slot /></button>',
    props: ["isDisabled"],
    methods: {
        doSomething() {
            Event.$emit('somethingWasDone');
        }
    },
});

Upvotes: 0

Related Questions