I created a custom directive to handle select2 in VueJs. The code below works when I am binding a select to a data property in my viewmodel that is not a propert of an object within data.
Like this.userId but if it is bound to something like this.user.id, it would not update the value in my viewmodel data object.
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>
Related fiddle: https://jsfiddle.net/raime910/rHm4e/4/
When you using 1st level $data
's-property, it accessing to $data
object directly through []
-brackets
But you want to pass to selected
-directive the path to nested object, so you should do something like this:
// 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>
Suppose we have two $data
's props: valOrObjectWithoutNesting
and objLvl1
:
data: function(){
return{
valOrObjectWithoutNesting: 'let it be some string',
objLvl1:{
objLvl2:{
objLvl3:{
objField: 'primitive string'
}
}
}
}
}
Variant with 1st level $data's-property:
<select v-selected="valOrObjectWithoutNesting">
// Now this code:
vnode.context.$data[binding.expression] = select.val();
// Equals to:
vnode.context.$data['valOrObjectWithoutNesting'] = select.val();
Variant with 4th level $data's-property:
<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
So the deepSet
function in my code above "converting" $data['objLvl1.objLvl2.objLvl3.objField']
to $data['objLvl1']['objLvl2']['objLvl3']['objField']
.
As you see, as I mentioned in comments to your question, when you want make select2-wrapper more customisable, the directive-way much more complicated, than separate component-way. In component, you would pass as much configuration props and event subscriptions as you want, you would avoid doing side mutations like vnode.context.$data[binding.expression]
and your code would become more understandable and simpler for further support.