Make VueJS and jQuery play nice

21,461

Solution 1

The way to make Vue play nicely with other DOM-manipulating toolkits is to completely segregate them: if you are going to use jQuery to manipulate a DOM widget, you do not also use Vue on it (and vice-versa).

A wrapper component acts as a bridge, where Vue can interact with the component and the component can manipulate its internal DOM elements using jQuery (or whatever).

jQuery selectors outside of lifecycle hooks are a bad code smell. Your validatePhoneNumber uses a selector and a DOM-manipulating call, but you are using Vue to handle keydown events. You need to handle everything on this widget with jQuery. Don't use Vue to set its class or phone_number or handle its events. Those are all DOM manipulations. As I mentioned, if you wrap it in a component, you can pass props to the component and from those props you can use jQuery to set class and phone_number.

Solution 2

I think the reason behind it is that when the keydown event is fired, the internal workings of the plugin kicks in and does not immediately expose the phone values. This results in a race condition where you prematurely retrieves the phone number value before it is being updated internally by the plugin itself.

This issue can simply be resolved by listening to other events, such as keyup or input:

const app = new Vue({
  el: '#app',

  data: {
    phone_number: "",
    validPhone: false
  },


  methods: {
    validatePhoneNumber: function() {
      var phone_element = $('#phone');
      var validPhoneNo = phone_element.intlTelInput("isValidNumber");
      var phoneNo = phone_element.intlTelInput("getNumber");
      console.log(phoneNo + ' -> ' + validPhoneNo); // I am interested in both values
    }
  },


  mounted: function() {
    $('#phone').intlTelInput({
      utilsScript: "js/utils.js",
      initialCountry: "auto",
      geoIpLookup: function(callback) {
        $.get('https://ipinfo.io', function() {}, "jsonp").always(function(resp) {
          var countryCode = (resp && resp.country) ? resp.country : "";
          callback(countryCode);
        });
      },
      preferredCountries: []
    });
  }
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/11.0.10/css/intlTelInput.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/11.0.10/js/utils.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/11.0.10/js/intlTelInput.min.js"></script>

<div id="app">
  <div class="row">
    <div class="col-sm-offset-2 col-sm-6">
      <input class="form-control" @input="validatePhoneNumber" :class="{validInput: validPhone }" name="phone" value="" ref="phone_element" :phone_number="phone_number" type="text" id="phone" />
    </div>
  </div>
</div>

I have to admit that this is still not the ideal solution, because there might still be a race condition going. The only way to make sure that no race condition exists is to wait for the plugin to trigger a custom callback once the phone number is parsed and validated, which you can listen on the input element in VueJS. Right now I see that the plugin only has a custom callback for country change.

Share:
21,461

Related videos on Youtube

Angelin Calu
Author by

Angelin Calu

The short list of interesting things for me would look something like: PHP, MySQL, JavaScript, TypeScript, HTML, CSS, Python, C#, NodeJS, NPM, Laravel, VueJS, React, jQuery, VBA, Excel, VSCode, DevOps, CI/CD, ... OS related info for Windows, MacOS and Linux. I have switched from being a shipbuilding engineer to being a full-time full-stack software engineer.

Updated on April 07, 2020

Comments

  • Angelin Calu
    Angelin Calu about 4 years

    This question is related/similar to Using intl-tel-input and vuejs2 together , which is still unanswered.

    And VueJS Use prop as data-attribute value which has a solution, but explains a bit the "environment".

    So, long story short, I am dynamically setting up a new bootstrap tab (Title and URL), and then trying to (re)bind some functionality using jQuery.

    Adding the following line https://github.com/thecodeassassin/bootstrap-remote-data/blob/master/js/bootstrap-remote-tabs.js#L258 in my Vue method will apply the functionality, but only if I trigger the change twice.

    The same issue with the first (unanswered) question.

    Could someone explain a bit how the things work between vueJS and jQuery ? And how to solve things, hopefully without needing to rewrite jQuery packages.

    If I console.log my variables just seem one step behind.

    LE:

    I have prepared a Pen for the related mentioned issue: https://codepen.io/AngelinCalu/pen/LWvwNq

    • victor
      victor about 7 years
      Where is your Vue code? Can you please be more specific with your question? in my experience i have only had to use jquery with Vue in order to take advantage of jquery datepicker.
    • WilomGfx
      WilomGfx about 7 years
      Need some code, we can't help you without knowing how you did it....
    • Angelin Calu
      Angelin Calu about 7 years
      Please consider the code from here: stackoverflow.com/questions/43236848/…, with the mention that I am using github.com/thecodeassassin/bootstrap-remote-data, and with var remoteTabsPluginLoaded = new RemoteTabs(customSettings); added in showDetail method.
    • Roy J
      Roy J about 7 years
    • Angelin Calu
      Angelin Calu about 7 years
      @RoyJ For sure it's the same issue, but still very unclear in terms of what's happening there when creating a wrapper component. Could you elaborate into some answer maybe giving some generic example.
    • Terry
      Terry about 7 years
      Your Codepen seems to be working properly--what is the issue there?
    • Angelin Calu
      Angelin Calu about 7 years
      @Terry Open the Console and check the log. You will see that it's one step behind the input.
    • Terry
      Terry about 7 years
      What if you listen to the onInput event (i.e. @input) instead of onKeydown? It worked for me.
    • Angelin Calu
      Angelin Calu about 7 years
      @Terry WOW!! :O I wasn't expecting that. Why does it work? How about if I have a @onclick trigger as in the other example?
    • Terry
      Terry about 7 years
      From which element do you intend to bind the click even to? Another component? Another DOM element that is not part of VueJS virtual DOM?
    • Angelin Calu
      Angelin Calu about 7 years
      Well, The phone validation example is somehow an abbandoned idea. I am now trying to create a remote bootstrap tab dinamically using vueJS. And then load the contents using some jquery plugin. The links are all in the question.