jackocnr / intl-tel-input

A JavaScript plugin for entering and validating international telephone numbers

Home Page:https://intl-tel-input.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Disable and remove autoFormat feature

jackocnr opened this issue · comments

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:

  • First and foremost, we are hacking libphonenumber to get this to work, which is dangerous. It was not made for this purpose, they do not support our use case, and they have explicitly advised us against using it this way.
  • When the user types we have to call preventDefault so we can control what happens, but this means we lose a bunch of useful native events like change and input, which other devs may depend on.
  • Last time I checked, autoFormat didn't work in Android Cordova apps, or in IE Mobile (see Browser Compatibility section of readme). Who knows what other browsers/versions/platforms make it not work properly.
  • There is no way around this incredibly counter-intuitive UX issue: #322 where you cannot type the first character from the placeholder. This affects 49 countries including the USA - see #346 (comment).
  • Another minor issue: even if we find a fix for the above issue, it will lead to a new one: when a country placeholder starts with a formatting char (e.g. US starts with an open bracket), if you press that char to try and start the number, you will get a red flash as that is not an accepted key, but then it will still add the char because autoFormat does a lookahead and adds any upcoming formatting chars on each keypress, which leads to a confusing experience.
  • Then there's all the issues we have already come across, which suggests to me that there are so many more we don't yet know about. We get bug reports for the popular countries, but much less for the unpopular ones, which means most likely they are buggy and we just don't know about it.
  • Finally, restricting what the user can do in a regular <input> is considered bad practice by a lot of people (I have yet to find an example of Google or Facebook doing anything like this). As we have seen, this approach is very prone to bugs, and when there are bugs it can completely ruin the UX, or even render the whole input useless. It's a very dangerous approach.

Notes

  • We should still keep a formatNumber function available, which should be used with setNumber etc.
  • This change would render the allowExtensions option useless, so we would remove that too.
  • This change would also close issues: #190, #262 and #322
  • Another perk is that this should significantly decrease the file size of the plugin.

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

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.

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 :)

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

Implemented this change in v8.0.0.

RIP autoFormat <3

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

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

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.

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!

@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.

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?

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.

@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: #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.

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.

@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.

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

Let's review:

  • It doesn't work properly for 20% of users (and potentially waaaay more since US is in that list).
  • Users are humans, and humans are intrinsically unique: They will type (555) 555 555, (555)-555-555, 555555555, 555.555.5555, +1 555-555-555. There is no organic and global pattern (unlike with credit card numbers). Enforcing one format ensures the frustration of many.
  • Updating value while typing is not considered a good UX:

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

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

@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.

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

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

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

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.

@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).

@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

@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.

@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?

@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();
});

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?

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

@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...

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

How to get country with double zeroes?

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 👍

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"));
});

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'
	});

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
        }
    }
}

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

commented

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

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.