rhoward
rhoward

Reputation: 313

How to catch events across multiple child Vue components

I am building a form framework in vue. I have components for each field type. Each field type component uses this.$emit to communicate changes with the parent component.

I am able to trigger events in the parent component using v-on directives as follows:

<template>
<div v-if="fieldsLoaded">
    <form-select :field="fields.title" v-on:updated="validate" ></form-select>
    <form-input :field="fields.first_name" v-on:updated="validate" ></form-input>
</div>
</template>

However, I don't want to have to manually specify that every component should trigger the validate method individually.

How can I have the parent component listen for the updated emit across all its child components?

Edit: I'm looking for something like the below, though $on only catches emits that occur within the same component, rather than its children

created: function(){
    this.$on('updated',validate)
}

Upvotes: 3

Views: 4855

Answers (2)

Saurabh
Saurabh

Reputation: 73609

For your case you can use v-model like following:

<template>
<div v-if="fieldsLoaded">
    <form-select v-model="fields.title" :validate="validate" ></form-select>
    <form-input v-model="fields.first_name" :validate="validate" ></form-input>
</div>
</template>

v-model is essentially syntax sugar for updating data on user input events.

<input v-model="something">

is just syntactic sugar for:

<input v-bind:value="something" v-on:input="something = $event.target.value">

You can pass a prop : value in the child components, and before changing input field call a function to validate which is also passed as a prop.

Vue.component('form-select', {
  props: ['options', 'value', 'onChange', 'validate'],  //Added one more prop
  template: '#your-template',
  mounted: function () {
  },
  methods: {
    change (opt) {
      if (this.validate !== undefined) {
        var isValid = this.validate(this.value)
        if(!isValid) return;
      }
      this.$emit('input', opt)
    },
  },
})

Upvotes: -1

Roland
Roland

Reputation: 27729

The best way is to use event bus or even better in my opinion vuex.

For the first case take a look here

For the second here

With event bus you can emit an event, and listen to that event whenever you want(at parent,child even in the same component)

Vuex It serves as a centralized store for all the components in an application and you can have properties in that store,and you can use and manipulate them.

Example with event Bus:

main.js:

import Vue from 'vue'
import App from './App.vue'

export const eventBus = new Vue();

new Vue({
  el: '#app',
  render: h => h(App)
})

User Component

<template>
  <button @click="clicked">Click me to create event</button>
</template>
<script>
    import { eventBus } from './main'
    export default {
    name: 'User',
    methods: {
        clicked() {
        eventBus.$emit('customEvent', 'a text to pass')
      }
    }
  }
</script>

Admin component

<template>
 <p>The message from event is: {{message}}</p>
</template>
<script>
    import { eventBus } from './main'
    export default {
    name: 'Admin',
    data: () => ({
        message: ''
    })
    created() {
        eventBus.$on('customEvent', dataPassed => {
        this.message = dataPassed
      }
    }
  }
</script>

Take a look to this tutorial to learn Vuex

Upvotes: 5

Related Questions