Javascript VueJs指令双向绑定

Javascript VueJs指令双向绑定,javascript,vue.js,vuejs2,jquery-select2,vue-directives,Javascript,Vue.js,Vuejs2,Jquery Select2,Vue Directives,我创建了一个自定义指令来处理VueJs中的select2。当我将select绑定到viewmodel中不是数据中对象属性的数据属性时,下面的代码会起作用 类似于this.userId,但如果它绑定到类似于this.user.id的对象,则不会更新我的viewmodel数据对象中的值 Vue.directive('selected', { bind: function (el, binding, vnode) { var key = binding.expr

我创建了一个自定义指令来处理VueJs中的select2。当我将select绑定到viewmodel中不是数据中对象属性的数据属性时,下面的代码会起作用

类似于this.userId,但如果它绑定到类似于this.user.id的对象,则不会更新我的viewmodel数据对象中的值

Vue.directive('selected', {    
    bind: function (el, binding, vnode) {    
        var key = binding.expression;    
        var select = $(el);    

        select.select2();    
        vnode.context.$data[binding.expression] = select.val();    

        select.on('change', function () {    
            vnode.context.$data[binding.expression] = select.val();    
        });    
    },    
    update: function (el, binding, newVnode, oldVnode) {    
        var select = $(el);    
        select.val(binding.value).trigger('change');    
    }    
});

<select v-selected="userEditor.Id">
   <option v-for="user in users" v-bind:value="user.id" >
       {{ user.fullName}}
   </option>
</select>
Vue.directive('selected',{
绑定:函数(el,binding,vnode){
var key=binding.expression;
变量选择=$(el);
select.select2();
vnode.context.$data[binding.expression]=select.val();
select.on('change',function(){
vnode.context.$data[binding.expression]=select.val();
});    
},    
更新:函数(el,binding,newVnode,oldVnode){
变量选择=$(el);
select.val(binding.value).trigger('change');
}    
});
{{user.fullName}
相关小提琴:

当您使用第一级
$data
的属性时,它通过
[]
-括号直接访问
$data
对象

但您希望将嵌套对象的路径传递给
selected
-指令,因此应执行以下操作:

// source: https://stackoverflow.com/a/6842900/8311719
function deepSet(obj, value, path) {
    var i;
    path = path.split('.');
    for (i = 0; i < path.length - 1; i++)
        obj = obj[path[i]];

    obj[path[i]] = value;
}

Vue.directive('selected', {    
bind: function (el, binding, vnode) {    
    var select = $(el);    

    select.select2();    
    deepSet(vnode.context.$data, select.val(), binding.expression);    

    select.on('change', function () {    
        deepSet(vnode.context.$data, select.val(), binding.expression);
    });    
},    
update: function (el, binding, newVnode, oldVnode) {    
    var select = $(el);    
    select.val(binding.value).trigger('change');    
}    
});

<select v-selected="userEditor.Id">
<option v-for="user in users" v-bind:value="user.id" >
   {{ user.fullName}}
</option>
</select>
具有第一级$data属性的变量:

<select v-selected="valOrObjectWithoutNesting">

// Now this code:
vnode.context.$data[binding.expression] = select.val();
// Equals to: 
vnode.context.$data['valOrObjectWithoutNesting'] = select.val();
<select v-selected="objLvl1.objLvl2.objLvl3.objField">

// Now this code:
vnode.context.$data[binding.expression] = select.val();
// Equals to: 
vnode.context.$data['objLvl1.objLvl2.objLvl3.objField'] = select.val(); // error here

//现在这个代码:
vnode.context.$data[binding.expression]=select.val();
//等于:
context.$data['valOrObjectWithoutNesting']=select.val();
具有第四级$data属性的变量:

<select v-selected="valOrObjectWithoutNesting">

// Now this code:
vnode.context.$data[binding.expression] = select.val();
// Equals to: 
vnode.context.$data['valOrObjectWithoutNesting'] = select.val();
<select v-selected="objLvl1.objLvl2.objLvl3.objField">

// Now this code:
vnode.context.$data[binding.expression] = select.val();
// Equals to: 
vnode.context.$data['objLvl1.objLvl2.objLvl3.objField'] = select.val(); // error here

//现在这个代码:
vnode.context.$data[binding.expression]=select.val();
//等于:
vnode.context.$data['objLvl1.objLvl2.objLvl3.objField']=select.val();//这里出错
因此,上面代码中的
deepSet
函数将
$data['objLvl1.objLvl2.objLvl3.objField']
转换为
$data['objLvl1']['objLvl2']['objLvl3']['objField']


正如您所看到的,正如我在对您的问题的评论中提到的,当您想要使select2包装器更具定制性时,指令方式要比单独组件方式复杂得多。在组件中,您可以根据需要传递尽可能多的配置道具和事件订阅,您可以避免执行诸如
vnode.context.$data[binding.expression]
之类的附加突变,并且您的代码将变得更易理解和更简单,以便进一步支持。

自定义指令非常好,除了使用
插入的
钩子而不是
绑定
。改编自

要绑定到对象属性,最简单的方法是将其包装在计算setter中并绑定到该setter

注意,“深度设置”似乎不起作用。问题是变化检测,这是计算机setter所克服的。(请注意,
on('change')
函数是jQuery而不是Vue。)

console.clear()
Vue.指令('选定'{
插入:函数(el、绑定、vnode){
变量选择=$(el);
选择
.select2()
.val(binding.value)
.trigger('更改')
.on('change',function(){
if(vnode.context[binding.expression]){
vnode.context[binding.expression]=select.val();
}
})
},
});
var vm=新的Vue({
el:“#我的应用程序”,
计算:{
selectedValue:{
get:function(){返回this.myObj.type},
set:function(value){this.myObj.type=value}
}
},
数据:{
selectedVal:0,
myObj:{type:3},
选择:[{
id:1,
文本:“测试1”
}, {
id:2,
文本:“测试2”
}, {
id:3,
文本:“测试3”
}]
}
});

测试下拉列表({myObj.type}})
{{opt.text}}

你能分享这个的现场演示吗?为什么你要用指令而不是自定义的Vue组件?嗯,组件方式更简单——你不需要使用这个hack
vnode.context.$data
,你也可以使用
创建的
挂载的
钩子等等。几周前,我在Vu中为一些jquery插件实现了包装器e:我第一次采用指令方式——很快它就变得太复杂了,所以我进行了重构以分离vue组件,现在它很简单,工作也很好。顺便问一下,您是否看到了这个本机vue组件库:?--我在我的项目中使用了它。它已经是vue组件,所以不需要包装器。另外,请注意这一刻:您正在使用
v-for
对于不带
的对象:key
-此处的属性:
。应该是这样:
(请参阅此文档:)。简而言之,如果您是迭代对象,而不是原始类型,那么Vue反应性可能会出现意外错误。(我花了整整一天的时间在项目中找出相同的遗漏)。您可以使用HTML select元素并设置
is
属性,使其成为select2组件。