angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.83k stars 27.51k forks source link

Email validation not working with Chrome autofill #1072

Closed kylefinley closed 12 years ago

kylefinley commented 12 years ago

As discussed here: https://groups.google.com/forum/?fromgroups#!topic/angular/GQVX5F3v7Xk

When using Chrome autofill angular is not reevaluating the email address after the fill, therefore, it believes it to be invalid.

Here's a jsfiddle demonstrating the issue: http://jsfiddle.net/kylefinley/DuM6D/14/

There's a screen shot in the linked thread.

This issues appears in the following Chromium browsers:

Iron Version 19.0.1100.0 (139545) Chromium Version 21.0.1180.0 (142701) Chrome Version 19 - 20.0.1132.34 beta

Please let me know if you are not able to reproduce this.

Thank you,

Kyle

IgorMinar commented 12 years ago

I can't reproduce this on MacOS 10.7 with Chrome 20.0.1132.34 beta or 21.0.1179.0 canary.

If anything this is a browser bug - the browser doesn't trigger the "input" event when the autofill is committed by selecting the email address from the list and hitting enter or clicking on it.

I'm going to close this issue for now, but if you are still affected by it, please do let us know. You could also try to test the latest canary version and see if you can reproduce it there.

If we do need to follow up on this issue, it will most likely be by refiling this as a Chrome bug, but for that we need to be able to reproduce it.

kylefinley commented 12 years ago

I figured out what is going on. The reason that it's difficult to reproduce is because there are two ways that Chrome will populate fields.

  1. With tab completions - this uses the name of the field to bring up a list of options.
  2. Using Autofill. When autofill is used the background color of the input will be changed to yellow. Also, the words "Chrome Autofill settings" appears at the bottom of the drop down list.

In this case Autofill is creating the problem. Autofill only populates the form when the following two conditions are met.

  1. The user has saved information in their autofill profile. ( chrome://chrome/settings/autofill )
  2. The form has a method="POST" attribute.

So to reproduce:

  1. Visit: chrome://chrome/settings/autofill
  2. Click Add new street address...
  3. Fill in form including email
  4. Revisit http://jsfiddle.net/kylefinley/DuM6D/14/

When using autofill you should then see the issue.

This simplest solution is to remove the method="POST" from the form, which is the documented way to use Angular forms. The problem with this approach is it disregards User's Autofill information. If you still can reproduce this or you deem the work around acceptable, please feel free to leave the issue close. Now that I have a way to circumvent autofills behavior, I'm content.

robrbecker commented 12 years ago

I can recreate this issue on Chrome Stable 19 and Angular 1.0 and 1.0.1. I'm doing exactly what kylefinley was doing. The form method is POST and I'd like to let people use autofill if they have it. I'd settle for a way to trigger an angular update manually when they click "continue" button.

After some trial and error and re-reading Igor's post I discovered I can simply do this to get the browser to fire input events for things that may have been filled in by autofill but haven't updated. example: $('#myform input,myform select').trigger('input');

robrbecker commented 12 years ago

(Edit to fix typo) If you have a submit or click handler for the form submission you can trigger the input event on all your inputs so that angular will pick up the changes by autofill.

Fire input event on all inputs: $('#myform input,#myform select').each(function() { $(this).trigger('input'); });

Or you can try firing an input event when other events happen like change or blur: $('#myform input,#myform select').on('change blur', function() { $(this).trigger('input'); });

PSA: This probably has performance implications and over triggers events.

kylefinley commented 12 years ago

Thank you @robrbecker for investigating this. It's good to have an alternative that works with Chrome's Autofill.

johannesjo commented 11 years ago

@robrbecker: Thanks for sharing, but I'm running into the problem that I get

Error: $apply already in progress

It works for the automatically populated fields, but deletes the data for all others. Very strange. Maybe I'm doing something wrong, but I would definitely suggest, to not use this fix for production builds.

robrbecker commented 11 years ago

@johannesjo Make sure you register these event listeners only once at dom ready. For instance this is what I'm doing.

var app = angular.module('app',[]); app.run(function() { // Trigger input event on change to fix auto-complete $('input, select').on('change',function() { $(this).trigger('input'); }); });

Do your own testing on your own app of course.

malixsys commented 11 years ago

This works for me:

function MyController($scope) {
  setTimeout(function() {
    $scope.$apply(function () {
      $('input[ng-model]').trigger('change');
    });
  }, 250);
}

As long as jQuery is loaded before Angular, of course!!

You can also use $timeout...

ghost commented 11 years ago

I'm getting this in Chrome 28 now even without method="POST".

katalin2003 commented 10 years ago

I came here searching for a solution as i have the same problem. Removing method="POST" is not an option for me unfortunately.

malixsys commented 10 years ago

I now use the following directive:

    var ValidSubmit = ['$parse','$timeout', function ($parse, $timeout) {
        return {
            require: '^form',
            link: function(scope, element, attrs, form) {
                        form.$submitted = false;
                        var fn = $parse(attrs.validSubmit);
                        element.on('submit', function(event) {
                            var inputs = element.find("*[ng-model]")
                            for(var i=0; i < inputs.length; i++) {
                                var ele = inputs.eq(i);
                                var field = form[inputs[i].name];
                                if(field != void 0) {
                                    if(inputs[i].type == "checkbox" || inputs[i].type == "hidden") {
                                        field.$setViewValue(field.$viewValue);
                                    } else {
                                        field.$setViewValue(ele.val());
                                    }
                                }
                            }
                            scope.$apply(function() {
                                element.addClass('ng-submitted');
                                form.$submitted = true;
                                if(form.$valid) {
                                    fn(scope, {$event:event});
                                }
                            });
                        });
                    }
        }

    }]
    app.directive('validSubmit', ValidSubmit);
jaichandra commented 10 years ago

Hi malixsys,

Can you please also tell how you are using that directive? I tried adding it to the

element and it does bind the model but only after I click submit button. and then, I have to click submit again to submit the form. can this work 'onchange' of the input field value?

Thanks

malixsys commented 10 years ago

Check https://github.com/malixsys/matryoshka/blob/master/app/client/app/anonymous/views/state_login.html et. al. The validation appears only on submit...