3

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)
}
rhoward
  • 183
  • 2
  • 15

2 Answers2

5

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

Roland
  • 24,554
  • 4
  • 99
  • 97
  • 1
    Can you give a hint on how Vuex can be used in such a situation. I know Vuex and use it as a data store, but not able to surmise how it can solve this particular issue – Joel G Mathew Apr 11 '22 at 13:55
-1

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)
    },
  },
})
tony19
  • 125,647
  • 18
  • 229
  • 307
Saurabh
  • 71,488
  • 40
  • 181
  • 244
  • You are referencing this.validate() but would this not require the validate method to be defined within this child component? My intention was that I would keep the validation logic in the parent component so it could be shared between the various child components. – rhoward Jan 16 '17 at 16:14
  • Sorry, I just understood your point - you are saying that I can pass in the validation method from the parent. I guess that would work, but it doesn't reduce my code any. I've already got it working as in my original example, where I explicitly trigger the validate method in the parent for each field. What I'm aiming for is something generic that catches *all* emissions of validate from its children, without needing to make it explicit field by field. – rhoward Jan 16 '17 at 17:02
  • @rhoward By my approach you can move show code related to error message etc inside those components. Regarding your point related to reusing the code, you can look at [mixins](https://vuejs.org/v2/guide/mixins.html#ad), where you can try to put common functionality and reuse it in components. – Saurabh Jan 16 '17 at 18:08