Skip to content

Commit f8dcbfd

Browse files
committed
improve visibility of custom input components through the guide, fixes #563
1 parent a06e895 commit f8dcbfd

File tree

2 files changed

+70
-56
lines changed

2 files changed

+70
-56
lines changed

src/guide/components.md

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ There may be times when you want to listen for a native event on the root elemen
534534

535535
### Form Input Components using Custom Events
536536

537-
This strategy can also be used to create custom form inputs that work with `v-model`. Remember:
537+
Custom events can also be used to create custom inputs that work with `v-model`. Remember:
538538

539539
``` html
540540
<input v-model="something">
@@ -557,85 +557,85 @@ So for a component to work with `v-model`, it must:
557557
- accept a `value` prop
558558
- emit an `input` event with the new value
559559

560-
Let's see it in action:
560+
Let's see it in action with a very simple currency input:
561561

562562
``` html
563-
<div id="v-model-example">
564-
<p>{{ message }}</p>
565-
<my-input
566-
label="Message"
567-
v-model="message"
568-
></my-input>
569-
</div>
563+
<currency-input v-model="price"></currency-input>
570564
```
571565

572566
``` js
573-
Vue.component('my-input', {
567+
Vue.component('currency-input', {
574568
template: '\
575-
<div class="form-group">\
576-
<label v-bind:for="randomId">{{ label }}:</label>\
577-
<input v-bind:id="randomId" v-bind:value="value" v-on:input="onInput">\
578-
</div>\
569+
<span>\
570+
$\
571+
<input\
572+
ref="input"\
573+
v-bind:value="value"\
574+
v-on:input="updateValue($event.target.value)"\
575+
>\
576+
</span>\
579577
',
580-
props: ['value', 'label'],
581-
data: function () {
582-
return {
583-
randomId: 'input-' + Math.random()
584-
}
585-
},
578+
props: ['value'],
586579
methods: {
587-
onInput: function (event) {
588-
this.$emit('input', event.target.value)
580+
// Instead of updating the value directly, this
581+
// method is used to format and place constraints
582+
// on the input's value
583+
updateValue: function (value) {
584+
var formattedValue = value
585+
// Remove whitespace on either side
586+
.trim()
587+
// Shorten to 2 decimal places
588+
.slice(0, value.indexOf('.') + 3)
589+
// If the value was not already normalized,
590+
// manually override it to conform
591+
if (formattedValue !== value) {
592+
this.$refs.input.value = formattedValue
593+
}
594+
// Emit the number value through the input event
595+
this.$emit('input', Number(formattedValue))
589596
}
590-
},
591-
})
592-
593-
new Vue({
594-
el: '#v-model-example',
595-
data: {
596-
message: 'hello'
597597
}
598598
})
599599
```
600600

601601
{% raw %}
602-
<div id="v-model-example" class="demo">
603-
<p>{{ message }}</p>
604-
<my-input
605-
label="Message"
606-
v-model="message"
607-
></my-input>
602+
<div id="currency-input-example" class="demo">
603+
<currency-input v-model="price"></currency-input>
608604
</div>
609605
<script>
610-
Vue.component('my-input', {
606+
Vue.component('currency-input', {
611607
template: '\
612-
<div class="form-group">\
613-
<label v-bind:for="randomId">{{ label }}:</label>\
614-
<input v-bind:id="randomId" v-bind:value="value" v-on:input="onInput">\
615-
</div>\
608+
<span>\
609+
$\
610+
<input\
611+
ref="input"\
612+
v-bind:value="value"\
613+
v-on:input="updateValue($event.target.value)"\
614+
>\
615+
</span>\
616616
',
617-
props: ['value', 'label'],
618-
data: function () {
619-
return {
620-
randomId: 'input-' + Math.random()
621-
}
622-
},
617+
props: ['value'],
623618
methods: {
624-
onInput: function (event) {
625-
this.$emit('input', event.target.value)
619+
updateValue: function (value) {
620+
var formattedValue = value
621+
.trim()
622+
.slice(0, value.indexOf('.') + 3)
623+
if (formattedValue !== value) {
624+
this.$refs.input.value = formattedValue
625+
}
626+
this.$emit('input', Number(formattedValue))
626627
}
627-
},
628-
})
629-
new Vue({
630-
el: '#v-model-example',
631-
data: {
632-
message: 'hello'
633628
}
634629
})
630+
new Vue({ el: '#currency-input-example' })
635631
</script>
636632
{% endraw %}
637633

638-
This interface can be used not only to connect with form inputs inside a component, but also to easily integrate input types that you invent yourself. Imagine these possibilities:
634+
The implementation above is pretty naive though. For example, users are allowed to enter multiple periods and even letters sometimes - yuck! So for those that want to see a non-trivial example, here's a more robust currency filter:
635+
636+
<iframe width="100%" height="300" src="https://jsfiddle.net/chrisvfritz/1oqjojjx/embedded/result,html,js" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
637+
638+
The events interface can also be used to create more unusual inputs. For example, imagine these possibilities:
639639

640640
``` html
641641
<voice-recognizer v-model="question"></voice-recognizer>

src/guide/forms.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ But sometimes we may want to bind the value to a dynamic property on the Vue ins
296296
type="checkbox"
297297
v-model="toggle"
298298
v-bind:true-value="a"
299-
v-bind:false-value="b">
299+
v-bind:false-value="b"
300+
>
300301
```
301302

302303
``` js
@@ -360,3 +361,16 @@ If you want user input to be trimmed automatically, you can add the `trim` modif
360361
```html
361362
<input v-model.trim="msg">
362363
```
364+
365+
## `v-model` with Components
366+
367+
> If you're not yet familiar with Vue's components, just skip this for now.
368+
369+
HTML's built-in input types won't always meet your needs. Fortunately, Vue components allow you to build reusable inputs with completely customized behavior. These inputs even work with `v-model`! To learn more, read about [custom inputs](components.html#Form-Input-Components-using-Custom-Events) in the Components guide.
370+
371+
372+
373+
374+
375+
376+

0 commit comments

Comments
 (0)