14

I currently use Vue.JS 2.0 and I want to update the model off one Vue instance from an custom directive, but im looking a nice way to do it, this is because i trying to create an custom directive that implement JQueryUI-Datepicker the code is the follow:

<input type="text" v-datepicker="app.date" readonly="readonly"/>

Vue.directive('datepicker', {
  bind: function (el, binding) {
    $(el).datepicker({
      onSelect: function (date) {
        //this is executed every time i choose an date from datepicker
        //pop.app.date = date; //this work find but is not dynamic to parent and is very dirty
        Vue.set(pop, binding.expression, date); //this should work but nop
      }
    });
  },
  update: function (el, binding) {
    $(el).datepicker('setDate', binding.value);
  }
});

var pop = new Vue({
    el: '#popApp',
    data: {
        app: {
            date: ''
        }
    }
});

Someone know how to update pop.app.date in a dynamic way from the directive, i know that binding.expression return in this example app.date and date return the current date picked in the datepicker but i dont know how to update the model from the directive

David Arreola
  • 143
  • 2
  • 6

2 Answers2

5

This will do the trick:

// vnode (third argument is required).
bind: function (el, binding, vnode) {
    $(el).datepicker({
        onSelect: function (date) {
            // Set value on the binding expression.
            // Here we set the date (see last argument).
            (function set(obj, str, val) {
                str = str.split('.');
                while (str.length > 1) {
                    obj = obj[str.shift()];
                }
                return obj[str.shift()] = val;
             })(vnode.context, binding.expression, date);
         }
    });
},

Reference: https://stackoverflow.com/a/10934946/2938326

Community
  • 1
  • 1
Kamal Khan
  • 973
  • 11
  • 12
  • 1
    The self invoking function traverses the dot notation (e.g. app.date) dynamically by splitting on the dots and looping through until we reach the desired key (**date** in your example) and then sets its value based on the value you provide as the last argument. – Kamal Khan Mar 08 '17 at 20:38
0

Just to follow up on @Kamal Khan's answer (which works great).

I have just done the following and got it to work (below). This removes finding the object and relies on Vue's set functionality to set the value.

bind: function (el, binding, vnode) {
    $(el).datepicker({
        onSelect: function (date) {
             Vue.set(vnode.context, binding.expression, date);
         }
    });
},

My full directive is:

  Vue.directive("datepicker",{
    bind(el,binding, vnode) {
       console.log(binding);
       var self = el
      $(self).datepicker({
        dateFormat:'mm-dd-yy',
        onSelect: function (date) {
            Vue.set(vnode.context, binding.expression, date);
        }
    });      
    },
    updated: function (el,binding) {
    }
 });  

I can then call this in the template or html with:

 <input v-model="dtime" v-datepicker="dtime"> 

with dtime being my data model value.

Hope this helps somebody else as this drove me nuts.

timmyjl12
  • 29
  • 2
  • When i try this on the version 2.5.16 i got an Vue warn and did not work "Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option.". My first implementation is like @Kamal Khan's do it, i ended wrapping in a component and emiting the value – David Arreola Jan 15 '19 at 18:29
  • @DavidArreola can you share what you did on a codepen or something similar please :) ? – Freddie Dec 19 '19 at 18:18