vue.js 表单checkbox :checked属性嵌套绑定时失效

图片描述在做一个类似与多级菜单多选的组件,发现有3级菜单是,第二级:checked属性绑定失败

html:

<div id="scope">

<!--全选框-->
<label for="all1">全选</label>
<input id="all1" type="checkbox" name="all" v-bind:checked="isAllChecked()" @change="changeAllChecked($event)">
<!--每一个列表-->
<div v-for="item in list">
    <!--父标题-->
    <div>
        <label v-bind:for="item.listTitle">parent{{item.listTitle}}</label>
        <input type="checkbox" v-bind:name="item.listTitle" v-bind:checked="isTitleChecked(item)" @change="changeTitleChecked(item,$event)">
    </div>
    <p>{{isTitleChecked(item)}}</p>
    <!--子列表-->
    <div v-for="chk in item.listItem">
        <label v-bind:for="chk.id">son{{chk.name}}</label>
        <input type="checkbox" v-bind:id="chk.id" v-bind:value="chk.id" v-bind:checked="isTitleChecked(chk)" v-model="item.selected"  @change="changeTitleChecked(chk,$event)">
        <p>{{isTitleChecked(chk)}}</p>
        <div v-for="subchk in chk.listItem">
            <label v-bind:for="subchk.id">sub{{subchk.name}}</label>
            <input type="checkbox" v-bind:id="subchk.id" v-bind:value="subchk.id" v-model="chk.selected">
        </div>
        <p>{{chk.selected}}</p>
    </div>
</div>

</div>

Javascript:

var scope = new Vue({

    el:"#scope",
    data : {
        list:[
            {
                //用于判断是否展示子列表
                isShowListItem : false,
                //用于记录选中了哪些子项
                selected : [],
                //父标题
                listTitle : "保利南悦湾",
                //子列表
                listItem : [
                    {               //chk
                        id : 1,
                        name : "李小龙",
                        selected:[],
                        listItem:[
                            {           //subchk
                                id:11,
                                name:"叶问",
                            },
                            {
                                id:12,
                                name:"彭于晏",
                            }
                        ]
                    },
                    /*{
                        id : 2,
                        name : "周星驰",
                        selected:[],
                        listItem:[
                            {
                                id:4,
                                name:"吴孟达"
                            }
                        ]
                    },
                    {
                        id : 3,
                        name : "周杰伦",
                        selected:[],
                        listItem:[
                            {
                                id:5,
                                name:"费玉清"
                            }
                        ]
                    }*/
                ]
            }/*,
            {
                isShowListItem : false,
                selected : [],
                listTitle : "南庄万科城",
                listItem : [
                    {
                        id : 4,
                        name : "大魔王"
                    },
                    {
                        id : 5,
                        name : "老妖怪"
                    }
                ]
            }*/
        ]
    },
    methods:{
        /**
         * 当父标题状态变化时的处理方法
         * @param data  [当前项的data]
         * @param event [当前项的event]
         */
        changeTitleChecked : function (data,event) {
            if (event.target.checked === true) {
                data.listItem.forEach(function (item) {
                    data.selected.indexOf(item.id) === -1 && data.selected.push(item.id);
                })
            }else {
                data.selected = [];
            }
        },
        /**
         * 判断父标题选择状态
         * @param data        [当前项的data]
         * @returns {boolean}
         */
        isTitleChecked : function (data) {
            var _selected = data.selected;
            var _listItem = data.listItem;
            // 验证selected中是否含有全部的item的id 如果是 证明title要选中
            return _listItem.every(function (item) {
                return _selected.indexOf(item.id) != -1;
            });
        },
        /**
         * 全选框change事件的回调处理方法
         * @param event
         */
        changeAllChecked : function (event) {
            if (event.target.checked === true) {
                this.list.forEach(function (data) {
                    data.listItem.forEach(function (item) {
                        data.selected.indexOf(item.id) === -1 && data.selected.push(item.id);
                    })
                })
            }else {
                this.list.forEach(function (data) {
                    data.selected = [];
                })
            }
        },
        /**
         * 判断全选框选择状态
         * @returns {boolean}
         */
        isAllChecked : function () {
            return this.list.every(function (data) {
                return data.selected.length === data.listItem.length;
            });
        },
    }
})

son的那个checkbox 的checked属性绑定失败当把son那级下面的2个checkbox够勾选上了以后,son不能自动勾选上,但是把son勾选上,它下级的2个checkbox是可以勾选上的

阅读 25.1k
3 个回答

你代码里面的问题:
v-model其实就是v-bind:checked的语法糖(参考官方文档),所以你代码里面混用的v-bind:checked="xxx"v-model="xxx"的用法不是正确的,在subchk节点的操作被Vue内部处理成了v-model相关的逻辑了。所以你采用v-bind:checked来验证当前节点是否被选中的实现方式,我个人觉得这样是不对的

个人感觉,对于

<input type="checkbox" v-bind:checked="isTitleChecked(chk)"  v-model="item.selected" />

这段代码,vue应该是根据v-model去判断了当前的checkbox是否被选中的逻辑(即简单粗暴的理解为v-model比v-bind:checked的优先级更高,假如 v-bind:checked 先告诉了 vue 当前的input为checked=true, 但是之后v-model又告诉 vue 当前的input为checked=false, 最后vue以v-model的结果为准)。而接下来的

<p>{{isTitleChecked(chk)}}</p>

因为它的结果是什么就是什么,没人来反对(覆盖)它,所以它就很简单的返回结果就好了。因而最终的结果就是你看到的同一个isTitleChecked(chk), 在<input>里面不生效,而在<p>里面生效了。

你这种写法导致的这种错误现象,我还没理清楚~~~,待我后续再观察观察....

嗯,最近也是在做这样的一个功能。对于v-bind:checked="" v-model=""整个人也是迷啊。
“v-model其实就是v-bind:checked的语法糖”,是这么回事。
回头看看vue官网,会发现很多忽略的细节。

这个我通过v-mode绑定id无效,默认始终没有选中后来改成 :checked='id==sex.id'生效了
Vue.component ("input-sex", {

template : "<div>" +
        "<span v-for='(sex,index) in sexs'>" +
        "<input type='radio' name='sex' :checked='id==sex.id' :value='sex.id' @click='updateVal'><label>{{sex.id}}-{{sex.name}}</label>" +
        "</span></div>",
props : {
    sexs : {
        type : Array,
        default : []
    },
    init:{
        type : Object,
        default : {}
    }
},
data: function () {
    return {
        id : this.init.sex
    }
},
methods : {
    updateVal:function(e){
        this.$emit('change',e.target.value);
    }
},
watch:{
    init:{
        handler:function(newValue, oldValue) {
            console.log(newValue)
       },
       deep: true
    }
},
mounted : function () {
    //this.updateVal(this.sex);
}

});

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏