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?
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.