13

Jsfiddle

HTML:

<div id="testVue">

<input id="test" v-model="testModel"/>

<button @click="clickMe()">Click me</button>

<button @click="showValue()">Show value</button>

</div>

Javascript

var testVue = new Vue({
  el: '#testVue',

  data: {
    testModel: 'Hello Vue!'
  },

  methods: {

    clickMe: function() {
        $("#test").val("testing");
        console.log(this.testModel);
    },

    showValue: function() {
        console.log(this.testModel);
    }
  }
});

I totally understand I shouldn't do this in Vuejs $("#test").val("testing");, I should have do like this.testModel="testing";

But I can't understand why this basic binding doesn't work in Vuejs? I have quite a lot of HTML component that update the input box via jquery, for example Calendar, Autocomplete and Select2, all will update the Input box via non-vuejs way.

Any easy way to handle this?

3
  • 4
    I don't know VueJS, but I think it's somewhat like Angular? If that's the case, jQuery has no business here. It just interferes with Vue. Just remove jQuery and do things the Vue way. Commented Oct 26, 2017 at 13:12
  • I think your fiddle is working...I just changed to this.testModel= 'testing'; Commented Oct 26, 2017 at 13:20
  • @SantoshKumar, I know that, but my v-model is updated by other 3rd party component via jquery. I can't change those to do this this.testModel='testing'; Commented Oct 26, 2017 at 13:38

5 Answers 5

21

its too late to answer but for anyone stumbles on this in future can use my suggestion to work with .

I had read somewhere that This is a jQuery problem, trigger/val() doesn't dispatch native events in some cases. So we will dispatch native JS Event after changing values with jQuery val()

$("#test").val("testing")[0].dispatchEvent(new Event('input'))

Points to note :-

  • in Vue, text and textarea elements use input event and checkboxes, radio and select uses change as an event. So dispatchEvent accordingly;
  • i assume that using id selector $("#test") you are applying this to single dom element thats why i have choosed [0] th element. if you are intending it to multiple elements then you can use for loop to dispatchEvent on al.

reference :- Vue Issues on Github

Sign up to request clarification or add additional context in comments.

Comments

8

Best way is to let jQuery change the data instead of the input val to keep everything synced.

Like this:

let mainState = { inputVal: 'testing' };

let app = new Vue({
  el: '#app',
  data: mainState,
  methods: {
    vueThing() {
      this.inputVal = 'Vue value';
    }
  }
}); 

$('#jqThing').on('click', function() {
  mainState.inputVal = 'jQuery Value';
});

https://jsfiddle.net/6evc921f/

Or wrap the jQuery elements constructors in Vue components and just use them in Vue enhanced pages

Comments

3

just use the @change event:

<input @change="$emit('update:testModel', $event.target.value)" id="test" v-model="testModel"/>

You could also use the .lazy modifier:

<input id="test" v-model.lazy="testModel"/>

as stated by the docs: By default, v-model syncs the input with the data after each input event (with the exception of IME composition as stated above). You can add the lazy modifier to instead sync after change events.

1 Comment

v-model.lazy will update data only when input loses focus or enter is pressed because it modify event from @input to @change
2

It doesn't work because v-model uses the input event by default which is not fired when you do:

$("#test").val("testing");

Which is one reason why you shouldn't inject anything in to the DOM directly unless you have a specific reason to do so.

Try typing in that box and clicking showValue and you will see that it has updated correctly because the input event has fired.

If you really want to go down this road (and I recommend that you don't), then you could just write your own custom directive, something like:

Vue.directive('model-inject', {
    bind: function(el, binding, vnode) {
        el.value = vnode.context[binding.expression];
        
        // Create inject event and add it to Vue instance (available by this.injectEvent)
        vnode.context.injectEvent = new CustomEvent("inject");
        // Attach custom event to el
        el.addEventListener('inject', function() {
            vnode.context[binding.expression] = el.value;
        });
        
        // Also bind input
        el.addEventListener('input', function() {
            vnode.context[binding.expression] = el.value;
        });
    },
    update: function(el, binding, vnode) {
        el.value = vnode.context[binding.expression];
    }
});

That binds a custom event called inject to the element as well as the default input event, so you use it like this:

<input id="test" v-model-inject = "testModel" />

Then when you update anything via jQuery you can do:

document.getElementById("test").dispatchEvent(this.injectEvent);

Here's the JSFiddle: https://jsfiddle.net/q591gn01/

Comments

0

Instead of using jQuery you could either create a global function to update your test model or you could look in to Vuex https://vuex.vuejs.org/en/intro.html

You should not try to update the DOM with jQuery when using Vue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.