Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VueJS: Why Trigger 'Input' Event Within 'Input' Event Handler?

Tags:

vue.js

emit

I'm learning VueJS. I'm figuring out their currency validation example code.

Vue.component('currency-input', {
  template: `
    <span>
      $
      <input
        ref="input"
        v-bind:value="value"
        v-on:input="updateValue($event.target.value)">
    </span>
  `,
  props: ['value'],
  methods: {
    // Instead of updating the value directly, this
    // method is used to format and place constraints
    // on the input's value
    updateValue: function (value) {
      var formattedValue = value
        // Remove whitespace on either side
        .trim()
        // Shorten to 2 decimal places
        .slice(
          0,
          value.indexOf('.') === -1
            ? value.length
            : value.indexOf('.') + 3
        )
      // If the value was not already normalized,
      // manually override it to conform
      if (formattedValue !== value) {
        this.$refs.input.value = formattedValue
      }
      // Emit the number value through the input event
      this.$emit('input', Number(formattedValue))
    }
  }
})

The $emit call at the bottom of the updateValue function, triggers an input event.

When I comment it out, the real time currency validation no longer works. So I realize it has a purpose.

But why trigger an input event inside an input event?

You'd think the input event would fire again, causing the updateValue handler to fire again, causing a stack overflow due to recursive calls.

I understand VueJS's much simpler $emit example code. It's just like Jquery's trigger function.

vm.$on('test', function (msg) {
  console.log(msg)
})
vm.$emit('test', 'hi')
// -> "hi"

But in the currency validation example, I do not understand why $emit is used the way it's used, and why it works the way it works.

Can somebody explain?

like image 605
Jay Avatar asked May 15 '17 14:05

Jay


People also ask

How do I stop event bubbling Vue?

You can use . prevent to intercept the default behavior of a form and use . stop to prevent event bubbling on a parent element.

What is Eventbus in Vue?

An Event Bus is nothing but a global Vue instance that is imported by the components involved in communication and passing data. It makes use of the $on, $emit, and $off properties of the Vue object to emit out events and pass on data.

Does Vue remove event listener?

In Vue we can listen for the hook:beforeDestroy event on component instance, and pass a callback which will remove the event listener.

What is $emit in Vue JS?

Vue $emit is a function that lets us emit, or send, custom events from a child component to its parent. In a standard Vue flow, it is the best way to trigger certain events.


1 Answers

The Emit call here is to allow you to hook into the event in parent contexts. The Input event is also used by the v-model directive to handle two way binding with components.

v-model='model' is essentially v-bind:value='model' v-on:input='model = $event.target.value' with some added bits to make it play nice. When you remove the this.$emit('input', Number(formattedValue)) You're removing the mechanism that updates the value outside the component.

EDIT: @Jay careful what you wish for sometimes

All elements in HTML have a series of native handlers for the common events; resize, load, unload, etc. These handle what to do when the page changes it's rendering and can be disabled or added onto, since the introduction of JavaScript browsers have used an event pump system that allows multiple functions to be attached to any event which run in sequence when the event is raised. An example being how you can have 3 functions run on resize to handle edge cases such as minimum/maximum size, screen orientation etc.

Form elements generally implement their own base event functions: keydown, keyup, mousedown, mouseup. These base functions invoke events to make our lives easier as developers, these being: input, blur, focus. Some have specialized events as in select elements implementing change, form tags implementing submit.

Input tags on focus capture keyboard input and display the text input cursor to indicate that it's ready to receive input. It adds in handlers for the tab keycode which finds the next available input and shifts focus to that element. The event pump style function system is great here as it allows you to bind to focus and do things like change the background color or border when the input is focused without having to implement the code for capturing input or displaying the cursor yourself.

Input tags also raise the input event when you type in them indicating that the input has changed, telling the browser to change the value and update the display so that the functionality expected by the user is consistent.

In the currency-input example we are adding the updateValue function to work with the native function and process the input value of the event, in the updateValue function we modify the string representation of the value and need someplace to put it. You could simply add a data property to hold the value and bind the input's value property to the data property allowing the currency-input to internally handle the display of the result but that would lock the value behind a private accessor and you would be unable to modify or retrieve the value of the resulting currency formatted value.

Using this.$emit('input', Number(formattedValue)) the updateValue function is acting similar to the native input tag by raising an event that can be captured by the parent context and worked with. You can store it in a value, use it as the basis for a function, or even ignore it completely though that may not help much. This allows you to keep track of the value of the input and modify it as needed or send it to the server, display it, etc.

It also ties into a few directives most pertinently v-model which is syntactic sugar to allow for a value property binding and an input event binding to a data property inside the current context. By providing a value prop and emitting an input event a custom element can act similar to a native form element in the systems of a Vue application. An extremely attractive feature when you want to package and distribute or reuse components.

It's a lot nicer to go:

...
<currency-input v-model='dollarValue'></currency-input>
<input v-model='dollarValue'>
...

Than to have to add in value and input bindings everywhere ergo:

...
<currency-input v-bind:value='dollarValue' v-on:input='updateDollarValue($event.target.value)'></currency-input>
<input v-bind:value='dollarValue' v-on:input='updateDollarValue($event.target.value)'>
...

Now that my weird rambling is done, I hope this helped with understanding some of the patterns and reasoning behind the currency-input example.

like image 128
Justin MacArthur Avatar answered Oct 13 '22 00:10

Justin MacArthur