自定义组件的 v-model
2.2.0+ 新增
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的。model 选项可以用来避免这样的冲突:
<body class="">
<div id="app-7">
<base-checkbox v-model="lovingVue"></base-checkbox>
</div>
<script src="js/vue.min.js"></script>
<script>
Vue.component("base-checkbox",{
model:{
prop:"checked",
event:"change"
},
props:{
checked:"Boolean"
},
template:`
<input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change',$event.target.checked)"/>
`
})
var app7 = new Vue({
el: '#app-7',
data:{
lovingVue:true
}
})
//正常情况下v-model="lovingVue" 是v-bind:value="lovingVue" 与 v-on:input="lovingVue=$event.target.value" 的语法糖,这种默认的情况在type="checkbox" 是不合适的,我们需要在组件中的model对象中指定event事件类型与prop为checked
//复选框一般只有一个属性checked,指选中或者不选中,向子组件传递也只要传递这个属性
</script>
</body>
将原生事件绑定到组件
你可能有很多次想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on
的 .native
修饰符:
<body class="">
<div id="app" class="demo">
<base-input v-on:focus.native="onFocus" label="标识" placeholder="请输入信息"></base-input>
</div>
<script src="js/vue-2.5.13.js"></script>
<script>
Vue.component('base-input', {
inheritAttrs: false,
props: ["label"],
template: `
<label>
{{label}}
{{$attrs.placeholder}}
{{$attrs["data-date-picker"]}}
<input v-bind="$attrs"/>
</label>
`
})
var app = new Vue({
el: "#app",
data: {
},
methods: {
onFocus: function() {
console.log("focused")
}
}
})
//v-on:focus.native="onFocus" 只作用在input上有效,此例中根元素是label,所以原生onFocus事件不起作用
//若要执行onFocus事件,模板的根元素修改成input template:` <input v-bind="$attrs"/>`
</script>
渲染为
<div id="app" class="demo">
<label>
标识
请输入信息
<input placeholder="请输入信息"></label>
</div>
这时,父级的 .native
监听器将静默失败。它不会产生任何报错,但是 onFocus
处理函数不会如你预期地被调用。
为了解决这个问题,Vue 提供了一个 $listeners
属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:
{ focus: function (event) { /* ... */ } input: function (value) { /* ... */ }, }
有了这个 $listeners
属性,你就可以配合 v-on="$listeners"
将所有的事件监听器指向这个组件的某个特定的子元素。对于类似 <input>
的你希望它也可以配合 v-model
工作的组件来说,为这些监听器创建一个类似下述 inputListeners
的计算属性通常是非常有用的:
完整例子
<body class="">
<div id="app" class="demo">
<base-input v-on:focus="onFocus" v-model="idata" label="标识" placeholder="please enter"></base-input>
<div>{{idata}}</div>
</div>
<script src="js/vue-2.5.13.js"></script>
<script>
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function() {
var vm = this
console.log(this.$listeners)
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function(event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
var app = new Vue({
el: "#app",
data: {
idata: "hi"
},
methods: {
onFocus: function() {
console.log("focused")
}
}
})
</script>
</body>
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
语法:Object.assign(target, ...sources)
this.$listeners
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
$listeners 继承父级的所有事件,例子中的focus和input,其中的input是隐含在v-model中(在父级中v-model="idata" 是 v-bind:value="idata" 与 v-on:input="idata=$event"的语法糖,其中$event是子组件抛出的值event.target.value)