jackocnr / intl-tel-input

A JavaScript plugin for entering and validating international telephone numbers. Now includes React and Vue components.
https://intl-tel-input.com
MIT License
7.55k stars 1.94k forks source link

Disable and remove autoFormat feature #346

Closed jackocnr closed 8 years ago

jackocnr commented 8 years ago

I have been back and forth about this a lot - I spent a lot of time implementing the autoFormat feature and fixing bugs, and I think it's a super cool feature, BUT I think it's time to disable and remove it for the following reasons:

Notes

I'm open to discussion, but at this point I'm pretty convinced, and would welcome a pull request that implemented this change.

nlwillia commented 8 years ago

I should preface this by acknowledging that I am not a domain expert on international phone numbers. I had not been exposed to E.164 before encountering this library. So what I assume to be sensible about how to deal with input issues comes from an engineering perspective, feedback from my own users and what experience I have with numbers in my own nationality.

I'm disappointed with the feedback we've gotten from libphonenumber about the problem of local formats. While I greatly respect their commitment to comprehensive and accurate metadata, I think they're limiting the practical applicability of their AsYouType formatter by only shipping it in a configuration that supports every configured format. There may be real world use cases internal to Google and elsewhere that benefit from that broader behavior, but if your goal is a full, standardized, unambiguous number then being able to choose to exclude local formats is a useful option to have when soliciting user input. My impression is not that they have rejected this line of reasoning so much as that they are satisfied with what they have and not interested in changing it. It would certainly take some work on their part to do so, and there may be more nuanced reasons from their experience why an E.164 subset for input wouldn't work out. Regardless, they've apparently chosen not to engage and that's their prerogative. I don't want to put words in anyone's mouth though; if I'm misinterpreting then I'm glad to hear more on the subject.

What I would argue is that the problem with the AsYouType formatter as it's being applied in intl-tel-input is not with the component itself, but with the data set it's operating on. The widget is clearly built to be metadata-driven, and concerns with specific formats can be cleanly and thoroughly addressed by removing those formats from the metadata file that it's compiled with. I don't think it's in any way an abuse of the formatter or broader library in the context of intl-tel-input to deploy only a subset of its metadata. It might be presumptuous to assume that we can identify all problem formats in an automated way without formal support for that interpretation by libphonenumber (though I suspect we can make a reasonable guess), but if someone seeking an E.164 result wants to remove known problem formats, then I don't see anything wrong with it aside from the hassle of maintaining those curated changes through releases of libphonenumber.

The as-you-type formatting capability adds some complexity and weight to intl-tel-input, but it enables support for unified extension input, and I think it's one of the distinguishing features of the widget. Libphonenumber is amazing as an abstract resource, but seeing it brought to life in intl-tel-input is just awesome, and the way the formatter works, when it works, is a real delight. I think it has practical value for the user as well, particularly for line-of-business situations where the user may not be familiar with the format of numbers in the country in question. I agree that the UX problem of unwanted formats causing confusion is a serious one, but I've been able to address that satisfactorily so far by just removing those formats in the build that I use. Nothing's going to prevent anyone who likes and depends on these features from forking and continuing to maintain libphonenumber releases on their own branch, but I think the library is stronger with them if there's a reasonable solution for restricting the formats.

jackocnr commented 8 years ago

Thank you for taking the time to write that. It's very encouraging to have someone feel so strongly about it. If we have people like you who are willing to put in the time to make this work, and we write a lot of tests to protect ourselves, then I think I would feel comfortable keeping autoFormat for now :)

jackocnr commented 8 years ago

As per my comment here, I have decided to remove the autoFormat feature until either libphonenumber decides to support our use case for their AsYouTypeFormatter (that would mean 1. formatting from the first digit instead of waiting until the third, and 2. allowing an option to specify the desired format e.g. full international format), or we find another lib that does the job.

This libphonenumber issue is about specifying a format: https://github.com/googlei18n/libphonenumber/issues/907

And this one talks about the 3 digit minimum: https://github.com/googlei18n/libphonenumber/issues/527

jackocnr commented 8 years ago

Implemented this change in v8.0.0.

RIP autoFormat <3

avimar commented 8 years ago

I'm sad to see this leave.. I thought this was an awesome bit of functionality.

jackocnr commented 8 years ago

Yes I'm sad too. But I've just done a bit more research, and it was actually broken for 49 countries including the USA. Here's the list (when nationalMode=true and numberType=FIXED_LINE, these countries' placeholder started with an open parenthesis, which the user couldn't type):

American Samoa: (684) 622-1234 Anguilla: (264) 461-2345 Antigua and Barbuda: (268) 460-1234 Armenia (Հայաստան): (010) 123456 Australia: (02) 1234 5678 Azerbaijan (Azərbaycan): (012) 312 34 56 Bahamas: (242) 345-6789 Barbados: (246) 412-3456 Bermuda: (441) 234-5678 Brazil (Brasil): (11) 2345-6789 British Virgin Islands: (284) 229-1234 Canada: (204) 234-5678 Cayman Islands: (345) 222-1234 Chile: (2) 2123 4567 Christmas Island: (08) 9164 1234 Cocos (Keeling) Islands: (08) 9162 1234 Colombia: (1) 2345678 Cuba: (07) 1234567 Dominica: (767) 420-1234 Dominican Republic (República Dominicana): (809) 234-5678 Ecuador: (02) 212-3456 Grenada: (473) 269-1234 Guam: (671) 300-1234 Hungary (Magyarország): (1) 234 5678 Indonesia: (061) 2345678 Ireland: (022) 12345 Jamaica: (876) 512-3456 Jordan (‫الأردن‬‎): (06) 200 1234 Lithuania (Lietuva): (8-312) 34567 Montserrat: (664) 491-2345 Northern Mariana Islands: (670) 234-5678 Pakistan (‫پاکستان‬‎): (021) 23456789 Paraguay: (21) 2345678 Peru (Perú): (01) 1234567 Philippines: (02) 123 4567 Puerto Rico: (787) 234-5678 Saint Kitts and Nevis: (869) 236-1234 Saint Lucia: (758) 430-5678 Saint Vincent and the Grenadines: (784) 266-1234 Sierra Leone: (022) 221234 Sint Maarten: (721) 542-5678 Slovenia (Slovenija): (01) 123 45 67 Tajikistan: (8) 372 12 3456 Trinidad and Tobago: (868) 221-1234 Turkey (Türkiye): (0212) 345 6789 Turkmenistan: (8 12) 34-56-78 Turks and Caicos Islands: (649) 712-1234 U.S. Virgin Islands: (340) 642-1234 United States: (201) 555-5555

jackocnr commented 8 years ago

Oh and one more note for people reading this thread: one alternative is to use the formatNumber method (provided by utils.js) to format the number on blur.

slavafomin commented 8 years ago

Hello!

I just wanted to say, that we are extremely disappointed and frustrated that you've decided to drop the support for phone number formatting. For us, this is the most useful feature of the intl-tel-input plugin, without it it's value is limited.

Actually, I'm not sure I can see the rational ground for this decision. As you stated in the documentation intl-tel-input support approximately 230 countries. The problem is experienced for just 49 of them. It's just a 21% of the total amount of affected users. In terms of population this number is probably even lesser. We are using this library for CIS countries which almost not affected at all. It works perfectly fine for Russian telephone numbers and our user base is 90% Russian right now.

I can't comprehend why you've decided to remove the entire feature when only 20% of users are affected. Wouldn't it be much smarter to just disable this feature for only affected countries?

The intl-tel-input is probably the best and only such UI library in the world right now, that perfectly solves the problem of entering correct phone numbers. We highly appreciate all your effort in developing and maintaining it. However, we've already spent a lot of our own time in order to integrate it into our new big project. I even created an integration library for Angular.js myself. It's a huge pain for me to see such a good and perfectly working plugin (for our users at least) to become crippled like this. And I'm appealing to you in order to find working solution to the problem instead of throwing the baby out with the bathwater.

1). Is it possible to disable this feature for only affected countries and preserve it for others?

2). If you really want to remove this functionality from the core package maybe we can re-introduce it as additional plugin? I.e. implement an extensible interface and use it to add this feature back. That way, the developers which wanted to have this functionality can have it by installing the additional JavaScript-file (even from remote repository).

We are very concerned with this problem and would really like to find a way to save this feature.

Thank you!

jackocnr commented 8 years ago

@slavafomin first off, let me join you in lamenting the loss of this feature - I put a lot of time and effort into it and trust me it was a difficult decision to remove it.

Regarding your suggestion to disable the feature just for the 49 broken countries - unfortunately that is not the only problem. It is broken for all countries on certain browsers, and for certain native events, and then there's the fact that the implementation is fundamentally a hack on an external lib who's maintainers have explicitly advised us against using it this way!

If you want to work on an additional plugin that adds this functionality then that's up to you, but as for having it in core: either we find a way to fix it for all countries (ideas welcome), or we leave it out.

slavafomin commented 8 years ago

I'm sorry I'm not sure how it's all working together. What exactly are we using from Google's library? I do believe it's data and formatting logic. What if we extract the required data from the original database and then implement the formatting logic ourselves? What's your estimate on this would it be hard to implement?

nlwillia commented 8 years ago

The asyoutypeformatter is here. As you can see, it's not a trivial component. My suggestion was to just modify the metadata it runs off of to get rid of unwanted variations. That turned out to be fairly effective, but there was still a lag problem where it wouldn't immediately accept or recognize the example format displayed in the placeholder text until the user had typed a few raw digits. The owner (who is entitled to his opinion) decided to draw the line there, and here we are.

v8 is a breaking change that I don't have time to address in my own application right now, so I'll be maintaining a v7 fork with the mitigations described in my PR for the short term. Unless we see an official solution from the libphonenumber side (which doesn't seem likely), a separate plugin project may be the best long term approach. It would have to maintain its own utils.js which is unfortunate, but there's no reason it couldn't bolt as-you-type and extension behaviors back on with keyboard events as long as you're tolerant of some edge cases.

jackocnr commented 8 years ago

@slavafomin we use a custom build of Google's libphonenumber for formatting/validation/placeholder numbers, which you can read about here: https://github.com/jackocnr/intl-tel-input/blob/master/src/js/utils.js.

@nlwillia has been very helpful in discussing this issue with me and coming up with ideas to try and get the autoFormat feature working properly for everyone (for which I am very grateful). Some of our discussion/ideas in this pull request: https://github.com/jackocnr/intl-tel-input/pull/357

If you guys have any questions about how anything works, or if you want to discuss any ideas, feel free to reach out. I would be thrilled if you found a reliable way to make this work for everyone!

Yer I think a separate project focusing on the utils.js file would be valuable. One that either found some clever way to manipulate libphonenumber to do what we need, or one that just copied the logic and then was maintained in-house.

caseyjhol commented 8 years ago

Yes I'm sad too. But I've just done a bit more research, and it was actually broken for 49 countries including the USA. Here's the list (when nationalMode=true and numberType=FIXED_LINE, these countries' placeholder started with an open parenthesis, which the user couldn't type):

I personally think this was more a problem with the placeholder, than the actual autoFormat feature itself. I completely removed the placeholder in my implementation in June of last year, for this same reason - the placeholder implied that you could type parenthesis and dashes.

I really don't think we need the placeholder (Google doesn't use one), especially if having it requires such a massive change. I'd prefer to keep autoFormat implemented, but simply hide the placeholder if it is enabled (or something as simple as "10 digits" so the user knows how many numbers to type).

I was deep in another project when these discussions were going on, otherwise I would've weighed in sooner. This just seems like a major change that's going to create a lot of extra work for many of the developers who use this plugin, to fix what is (as far as I can see), a pretty minor problem.

jackocnr commented 8 years ago

@caseyjhol thanks for weighing in. I really appreciate hearing everyone's opinions.

Regarding your suggestion to disable the placeholder: it's a matter of taste - I personally think placeholders are very useful. And removing the placeholder wouldn't solve the problem anyway - a lot of people will still try to type an open parenthesis as the first character and get a red flash, which is still bad UX.

Then there's all the other points I raised in my first comment e.g. hacking libphonenumber (which makes it unsupported and bug-prone), broken native events, unsupported in some browsers, other bugs, and a lot of people's opinions (including my own, and Google's for that matter) that you should not restrict what the user can type in an input - it's unexpected and can create a bad experience.

kirkas commented 8 years ago

The only question that should matter for autoFormat (or any feature) is: How does it affect and improve the user experience?

Let's review:

Ideally you'd let them type in the phone number in any format, and you'd have client and server side logic that could parse it out.

Link

Do not update what users enter when they're still typing. It fuddles up their ability to edit as they type, and it makes the field a moving target.

Link

lucasbmenezes commented 8 years ago

is there any example on how to use formatNumber from utils.js?

avimar commented 8 years ago

@lucasbmenezes

This code was based on riotjs but it should give you the idea:

var telInput =$('#number');
telInput.blur(function() {
    var currentFormat = (self.number.value[0]==="+") ? intlTelInputUtils.numberFormat.INTERNATIONAL : intlTelInputUtils.numberFormat.NATIONAL;
    self.number.value = telInput.intlTelInput("getNumber",currentFormat);
    }

The currentFormat part checks if INTL/NTL format and leaves the number in that format. I have it only work on Blur since format-as-you-type is MUCH more complicated.

jackocnr commented 8 years ago

@lucasbmenezes I have added examples to the utils.js wiki page.

iamchriswick commented 8 years ago

What if instead of removing autoFormat we use geoIpLookup to determine if autoFormat can be used?

Istiak1992 commented 8 years ago

is there any way to do like countrywise number validation for example for bangladesh i give input +8801717605698 but i am seeing that i can give +880171760569832442 how can i stop additional number for all all country ??pleaase help

WesCossick commented 8 years ago

Should this library not automatically format on blur, instead of having the developer listen for blur? Seems like that would relieve some of pain of not having it format while typing.

jackocnr commented 8 years ago

@iamchriswick thanks for your suggestion, but what if they select a different country? I don't want to have different inconsistent behavior depending on which country is selected. Also there are plenty of issues I mentioned in my original post that are not country-specific.

@Istiak1992 there is no way to restrict what the user types - this is widely considered to be bad practice. (if you'd like to discuss this further please open a separate issue).

@WesCossick it's a nice idea, but it's too specific a requirement to add this to the core plugin. Especially when it's just 2 lines to do it yourself. (if you'd like to discuss this further please open a separate issue).

jjxxxx commented 8 years ago

@jackocnr, Really appreciate you trying to make this work. 👍

Do you think masking based on country can help? Examples. http://digitalbush.com/projects/masked-input-plugin/ http://igorescobar.github.io/jQuery-Mask-Plugin/

We might still need to save number E.164 format.

Thoughts?

Thanks

redochka commented 8 years ago

@jjxxxx also https://github.com/RobinHerbots/jquery.inputmask

abuabdul commented 8 years ago

@jackocnr I felt bad about removing this feature. but i took the alternative to format on blur. It works great.

@Istiak1992 As long as the input is validated, why do you want to bother about what user types. If number is valid, go ahead, or stop the user. As simple as that.

epexa commented 7 years ago

@jackocnr Hello! Please see Inputmask Multi ! It have feature "The format of a list of masks". Example:

[
    { "mask": "+7(###)###-##-##", "cc": "RU", "name_en": "Russia" },
    { "mask": "+250(###)###-###", "cc": "RW", "name_en": "Rwanda" },
    { "mask": "+966-5-####-####", "cc": "SA", "name_en": "Saudi Arabia " },
    { "mask": "+966-#-###-####", "cc": "SA", "name_en": "Saudi Arabia" },
]

Maybe to connect it to your library?

aelkz commented 7 years ago

@jjxxxx @jackocnr It is not the best approach, but currently, I'm using the jquery-mask-plugin to set the format at onFocus() event using the current intl-tel-input placeholder.

1- Bootstrap the input telInput.intlTelInput({utilsScript: intl_tel_utils_path, nationalMode: true });

2- Create a function that will set the mask that will be called later.. var mask = function() { var placeholder = $(telInput).attr('placeholder'); console.log('placeholder value: '+placeholder); placeholder = placeholder.replace(new RegExp("[0-9]", "g"), "0"); console.log('mask value: '+placeholder); telInput.mask(placeholder, {reverse:false}); };

3- Set the magic when the input is focused or changed telInput.focus(function() { // setup mask only once from placeholder's masked value console.log('keyup fired'); mask(); });

johnnyshields commented 7 years ago

This is quite disappointing to see this feature removed, even if it wasn't perfect, have been using it my app for 3 years with no issue. Is there any chance of bring it back, perhaps with autoFormat = "onBlur", "asYouType", or "none" (default) variants?

johnnyshields commented 7 years ago

Another option is not to allow any parenthesis and just use spaces/dashes in the auto format (and placeholders)

jcugmas commented 7 years ago

@aelkz There is just one issue with your solution, when I change the country with a smaller placeholder/mask and I come back to the previous country, the placeholder is cut to the length corresponding to the smaller placeholder/mask. For example when you have France by default with a maxlength of 14 and you switch to another country with a maxlength of 8, when you come back to France, it stills on 8...

koresar commented 6 years ago

For those looking for a working code (just like me half an hour ago).

Here it is working in the International Mode.

phone number auto format

The +614 are mobile numbers in Australia. And +612 are land lines. They are formatted differently as you can see.

    var telInput = $("#mobile"),

    telInput.intlTelInput({
      utilsScript: "/js/intl-tel-input/utils.js",
      preferredCountries: ['AU'],
      nationalMode: false,
      formatOnDisplay: true // SET THIS!!!
    });

    telInput.on("keyup change", resetIntlTelInput);

    function resetIntlTelInput() {
      if (typeof intlTelInputUtils !== 'undefined') { // utils are lazy loaded, so must check
          var currentText = telInput.intlTelInput("getNumber", intlTelInputUtils.numberFormat.E164);
          if (typeof currentText === 'string') { // sometimes the currentText is an object :)
              telInput.intlTelInput('setNumber', currentText); // will autoformat because of formatOnDisplay=true
          }
      }
    }

PS: National mode seems to work too, but not as great:

phone number national mode

janakad commented 6 years ago

How to get country with double zeroes?

edgardluz commented 6 years ago

Another vote for restoring this feature some how. 99% of usage on my apps are US only. I'd love to use this plugin but auto-formatting is a must 👍

chatis commented 6 years ago

It could help someone,

I use the jQuery Plugin MASK for doing that.. and working very well you can find the plugin here : http://digitalbush.com/projects/masked-input-plugin

I use my code like this

$(".tel").intlTelInput({
      autoHideDialCode: false,
      autoPlaceholder : 'aggressive',
      initialCountry : 'CH',
      preferredCountries : ['CH', 'FR', 'DE', 'IT', 'CA'],
      separateDialCode : true,
  });

var telInput = $(".tel");

var reset = function() {
  telInput.removeClass("error");
  $("#tel_error").css("background", "url(../image_site/cross.png) no-repeat 10px 9px");
};

// on blur: validate
telInput.blur(function() {
  reset();
  if ($.trim(telInput.val())) {
    if (telInput.intlTelInput("isValidNumber")) {
      $("#tel_error").css("background", "url(../image_site/check.png) no-repeat 10px 9px");
    } else {
        $("#tel_error").css("background", "url(../image_site/cross.png) no-repeat 10px 9px");
    }
  }
});

telInput.on("countrychange", function(e, countryData) {
    $("#dial").val("+"+countryData.dialCode);
    $(".tel").val('');
    $(".tel").mask($(this).attr('placeholder').replace(/[0-9]/g, "9"));
});
hmzlam commented 5 years ago

i prefer to not use hacked libphonenumber after initiate your intl-tel-input

just use cleave js

var cleave = new Cleave('#telephone', {
        phone: true,
        phoneRegionCode: 'FR'
    });
multiwebinc commented 4 years ago

Here's the code @chatis provided above without jQuery since the library no longer requires it:

const telInput = document.querySelector("#telInput");

var iti = window.intlTelInput(telInput, {
      utilsScript: "/js/intl-tel-input/utils.js",
      preferredCountries: ['AU'],
      nationalMode: false,
      formatOnDisplay: true // SET THIS!!!
    }
);

telInput.addEventListener('keyup', formatIntlTelInput);
telInput.addEventListener('change', formatIntlTelInput);

function formatIntlTelInput() {
    if (typeof intlTelInputUtils !== 'undefined') { // utils are lazy loaded, so must check
        var currentText = iti.getNumber(intlTelInputUtils.numberFormat.E164);
        if (typeof currentText === 'string') { // sometimes the currentText is an object :)
            iti.setNumber(currentText); // will autoformat because of formatOnDisplay=true
        }
    }
}
sriramgroot commented 3 years ago

Here's the code @chatis provided above without jQuery since the library no longer requires it:

const telInput = document.querySelector("#telInput");

var iti = window.intlTelInput(telInput, {
      utilsScript: "/js/intl-tel-input/utils.js",
      preferredCountries: ['AU'],
      nationalMode: false,
      formatOnDisplay: true // SET THIS!!!
    }
);

telInput.addEventListener('keyup', formatIntlTelInput);
telInput.addEventListener('change', formatIntlTelInput);

function formatIntlTelInput() {
    if (typeof intlTelInputUtils !== 'undefined') { // utils are lazy loaded, so must check
        var currentText = iti.getNumber(intlTelInputUtils.numberFormat.E164);
        if (typeof currentText === 'string') { // sometimes the currentText is an object :)
            iti.setNumber(currentText); // will autoformat because of formatOnDisplay=true
        }
    }
}

Thanks @multiwebinc it worked for me

s-o-f commented 3 years ago

Here's the code @chatis provided above without jQuery since the library no longer requires it:

const telInput = document.querySelector("#telInput");

var iti = window.intlTelInput(telInput, {
      utilsScript: "/js/intl-tel-input/utils.js",
      preferredCountries: ['AU'],
      nationalMode: false,
      formatOnDisplay: true // SET THIS!!!
    }
);

telInput.addEventListener('keyup', formatIntlTelInput);
telInput.addEventListener('change', formatIntlTelInput);

function formatIntlTelInput() {
    if (typeof intlTelInputUtils !== 'undefined') { // utils are lazy loaded, so must check
        var currentText = iti.getNumber(intlTelInputUtils.numberFormat.E164);
        if (typeof currentText === 'string') { // sometimes the currentText is an object :)
            iti.setNumber(currentText); // will autoformat because of formatOnDisplay=true
        }
    }
}

Thanks @multiwebinc it worked for me

Be careful. This will not work with nationalMode: true. Example: Enter phone number: +79111188888 (can be copy-pasted) Try to erase last digit (press backspace) out

jackocnr commented 7 months ago

It's back baby! 🎈

formatAsYouType released in v19.1.0, and now live on the demo site for you to play with.

I thought of a workaround for the issue where the placeholder number in the US (and other countries) starts with an open bracket, but then it wont let the user type that character first. Annoyingly this is (still!) an issue with libphonenumber, as for some reason they auto-format the number differently to that (just hyphens until you hit 8 digits - don't ask!). BUT my workaround is this: if the user does decide to type their own formatting chars (e.g. an opening bracket), then we simply disable auto-formatting for them. I figure if they want to type their own formatting chars then let them. Or if they don't want to bother then we'll do it for them.

In general, this approach of allowing the user to type whatever they want solves a lot of the concerns I raised before, as we're not preventing the user from doing anything. Also we're no longer calling preventDefault so we're not swallowing any useful events that devs may be relying on.

One factor in re-adding this feature after so long is that I have started to see it be used by the big players e.g. Stripe (more info in this comment), and also Google Contacts (who do something similar to us - if the user types a formatting char, they just disable auto-format).

Also since I see so many people hacking this together themselves in ways that will be super buggy (e.g. by calling getNumber and setNumber on keyup, as above, or by using some third-party masking plugin, generating a mask for each country based on the example number, which is a massive oversimplification and will lead to all sorts of problems), I thought better to offer something official which while not perfect, will provide a much better UX.

Feedback is appreciated, though please start a new issue if you find anything.