6

I've been developing a generic Field component for use in forms in a Vue web app I'm developing. In my EditProduct component, for example, I have multiple instances of the Field component, some of them of type 'checkbox', some 'text', etc. Effectively what the checkbox variant does is generate something like this:

<div class="col-xs-2">
    <label>
        <input id='organic' name='organic' type="checkbox" v-model="product.organic"/>&nbsp;Organic
    </label>
</div>

from this:

<field :cols="2" name="organic" v-model="product.organic" type="checkbox"></field>

That's much simplified, but shows the general idea. What I'd like to be able to do is to pass something like a 'change' handler function name as a prop, like this:

<field :cols="2" name="organic" v-model="product.organic" type="checkbox" change="this.handleSomething"></field>

This would call a function in the EditProduct component. I'm not sure how to get this to work, though. I already have a change handler method in my Field component which is there to emit the notification event:

methods: {
    handleCheckbox: function (event) {
        this.$emit('input', event.target.checked);
        if(this.change){
           //do something here...
        }

    }
}, 

My idea was to test for the existence of a 'change' prop here and then do something, but all I have at this point is a string containing the name of a function. How do I actually use this to call a function, preferably with an argument derived from $event (such as $event.target.checked, say)?

1 Answer 1

8

Use $emit and v-on to comunicate from child to parent

Rather than passing in a handler, emit an event from your Field component when the internal value changes. Then, using v-on, you can register a handler for that event.

Here is the documentation for $emit. Usage: this.$emit('event-name', arguments...)

Here is the documentation for v-on. Usage: <component v-on:event-name="handler">


Also use v-on on the input element

You can also use v-on to register handlers for default events. For example, an input element will fire a change event when it is changed. Thus,

<input type="checkbox" v-on:change="onChange"/>

will cause onChange to fire when the checkbox changes. Note that arguments are automatically passed into the handler. So, in this case, the event is passed into onChange.


Example

Here is an example that puts it all together. In this example we see

  • a field component containing a single checkbox. It fires a change event when the checkbox is changed, and passes on event.target.checked
  • a root vue instance with two event handlers. They expect a boolean value (the result of event.target.checked)
  • in the html, two field components are created. Each one uses a different handler

Vue.component('field', {
  template: '#field',
  data: function() {
    return {
      value: null
    }
  },
  methods: {
    onChange: function(event) {
      // emit a change event
      // Provide event.target.checked as an argument
      this.$emit('change', event.target.checked);
    }
  }
})

new Vue({
  el: '#app',
  data: {
    selected1: false,
    selected2: false
  },
  methods: {
    changeHandler1: function(selected) {
      this.selected1 = selected;
    },
    changeHandler2: function(selected) {
      this.selected2 = selected;
    },
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>

<div id="app">
  <p>Selected1: {{selected1}}</p>
  <p>Selected2: {{selected2}}</p>
  
  <!-- Register a handler for the 'change' event -->
  <field v-on:change="changeHandler1"></field>
  <field v-on:change="changeHandler2"></field>
</div>

<template id="field">
  <!-- 
       By default, the input element fires a change event  
       when it is modified. The event is automatically passed  
       into the handler. 
  -->
  <input type="checkbox" v-on:change="onChange"/>
</template>

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, very helpful, and an especial thanks for including a working example. The solution was simpler than I thought it would be. One minor tweak was needed, though (unless I've misunderstood something). You emit a 'change' event in the Field component's 'onChange', which you need in order for the handlers like 'changeHandler1' to be fired. But you actually need to emit an 'input' event for the model to be updated. So I'm emitting both. I don't know if that's inefficient but it works.
this.$emit('change', event.target.checked); this.$emit('input', event.target.checked);
input is a special event that you can use if you want to use the v-model syntax. If you aren't using v-model syntax, you can name the events whatever you'd like. Read more about it here: vuejs.org/v2/guide/…
Notice that in the html, I was listening for an event called change with v-on:change. If the event was called something else, like my-special-event-name, then you would write v-on:my-special-event-name
Yes, that's the bit of documentation I was going from, and I am indeed using v-model when I use my Field component. E.g: <field :cols="2" name="organic" v-model="product.organic" type="checkbox"></field>. Out of curiosity, if I wished not to use v-model here, what would I do instead? LATER: Just saw your second comment, which may answer my question.

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.