Angularjs Chrome autocomplete dilemma

56,706

Solution 1

From the link added in the comment:Github Issue's

// Due to browsers issue, it's impossible to detect without a timeout any changes of autofilled inputs
// https://github.com/angular/angular.js/issues/1460
// https://github.com/angular/angular.js/issues/1460#issuecomment-28662156
// Could break future Angular releases (if use `compile()` instead of `link())
// TODO support select
angular.module("app").config(["$provide", function($provide) {
    var inputDecoration = ["$delegate", "inputsWatcher", function($delegate, inputsWatcher) {
        var directive = $delegate[0];
        var link = directive.link;

        function linkDecoration(scope, element, attrs, ngModel){
            var handler;
            // By default model.$viewValue is equals to undefined
            if(attrs.type == "checkbox"){
                inputsWatcher.registerInput(handler = function(){
                    var value = element[0].checked;
                    // By default element is not checked
                    if (value && ngModel.$viewValue !== value) {
                        ngModel.$setViewValue(value);
                    }
                });
            }else if(attrs.type == "radio"){
                inputsWatcher.registerInput(handler = function(){
                    var value = attrs.value;
                    // By default element is not checked
                    if (element[0].checked && ngModel.$viewValue !== value) {
                        ngModel.$setViewValue(value);
                    }
                });
            }else{
                inputsWatcher.registerInput(handler = function(){
                    var value = element.val();
                    // By default value is an empty string
                    if ((ngModel.$viewValue !== undefined || value !== "") && ngModel.$viewValue !== value) {
                        ngModel.$setViewValue(value);
                    }
                });
            }

            scope.$on("$destroy", function(){
                inputsWatcher.unregisterInput(handler);
            });

            // Exec original `link()`
            link.apply(this, [].slice.call(arguments, 0));
        }

        // Decorate `link()` don't work for `inputDirective` (why?)
        /*
         directive.link = linkDecoration;
         */
        // So use `compile()` instead
        directive.compile = function compile(element, attrs, transclude){
            return linkDecoration;
        };
        delete directive.link;

        return $delegate;
    }];

    $provide.decorator("inputDirective", inputDecoration);
    $provide.decorator("textareaDirective", inputDecoration);
    //TODO decorate selectDirective (see binding "change" for `Single()` and `Multiple()`)
}]).factory("inputsWatcher", ["$interval", "$rootScope", function($interval, $rootScope){
    var INTERVAL_MS = 500;
    var promise;
    var handlers = [];

    function execHandlers(){
        for(var i = 0, l = handlers.length; i < l; i++){
            handlers[i]();
        }
    }

    return {
        registerInput: function registerInput(handler){
            if(handlers.push(handler) == 1){
                promise = $interval(execHandlers, INTERVAL_MS);
            }
        },
        unregisterInput: function unregisterInput(handler){
            handlers.splice(handlers.indexOf(handler), 1);
            if(handlers.length == 0){
                $interval.cancel(promise);
            }
        }
    }
}]);

Solution 2

From: Developer.mozilla.org docs Turning_off_form_autocompletion

If an author would like to prevent the auto-filling of password fields in user management pages where a user can specify a new password for someone other than themselves, autocomplete="new-password" should be specified, though support for this has not been implemented in all browsers yet.

So, what makes it work for me:

  • set autocomplete="new-password" on the password field
  • set autocomplete="off" in the username field.

I hope that it works for you too :)

Solution 3

As said here, https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form

The Google Chrome UI for auto-complete requests varies, depending on whether autocomplete is set to off on input elements as well as their form. Specifically, when a form has autocomplete set to off and its input element's autocomplete field is not set, then if the user asks for autofill suggestions for the input element, Chrome might display a message saying "autocomplete has been disabled for this form." On the other hand, if both the form and the input element have autocomplete set to off, the browser will not display that message. For this reason, you should set autocomplete to off for each input that has custom auto-completion.

You need to set autocomplete="off" on both form and input

I don't think this is related to AngularJS

Solution 4

I had the same issue and found a very simple solution that just uses jQuery to grab the value on submit. In my controller I have the following:

$scope.username = "";
$scope.password = "";

$scope.login = function(){
    $scope.username = $("#username").val();
    $scope.password = $("#password").val();
    // Proceed as normal
};

There are some downsides, if you need to do validation etc but otherwise it's fine for smaller forms like this.

Solution 5

You could watch the email field value and everytime the value in that field is changing, you could trigger a "change"-event on the password field. This events trigger all the ng-model magic on that field and updates the model.

module.directive("autocompleteFor", function () {
    return {
        restrict: "A",
        link: function ($scope, $element, $attrs) {
            $scope.$watch($attrs.autocompleteFor, function () {
                $element.triggerHandler("change");
            })
        }
    }
});

With this directive you could handle that scenario like this:

<input type="email" name="email" ng-model="user.email">
<input type="password" autocomplete-for="user.email" name="password" ng-model="user.password"   required>
                       -----------------------------
Share:
56,706
Catfish
Author by

Catfish

Old enough to party. Nerdy by nature. Codementor Blog

Updated on October 26, 2020

Comments

  • Catfish
    Catfish over 3 years

    I have a simple login form which works just peachy unless you use Chrome's auto complete feature.

    If you start typing and use the auto complete feature and it auto populates your password, my angularjs model does not have any value for the password.

    I tried to turn autocomplete off by setting the attribute on the form autocomplete="off" but that doesn't seem to have any effect.

    How can I either: 1. Ensure that I can get the value if someone uses Chrome's auto-complete feature? 2. Disable Chrome's auto-complete feature?

    <form class="form-signin" name="form" ng-submit="login()" autocomplete="off">
    
            <h3>Login</h3>
    
            <input type="email" name="email" class="form-control" placeholder="Email address" ng-model="user.email" required autofocus>
            <input type="password" name="password" class="form-control" placeholder="Password" ng-model="user.password" required>
    
            <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    
     </form>
    
  • Catfish
    Catfish over 10 years
    The question is related to AngularJS in case there is a solution to capturing the auto-fill text in Angularjs. I was not aware of having to set autocomplete off on the input's though.
  • Catfish
    Catfish over 10 years
    I tried setting autocomplete="off" on both the form and the input elements and the auto complete is still autocompleting and the value still does not get set to my angular model.
  • Ryan Wheale
    Ryan Wheale over 10 years
    +1 This is the best solution on this page because it doesn't disable autocomptlete. Disabling autocomplete is like saying "hey user, screw you and your preferences". The only cross-browser solution is to poll. It's not difficult, and someone already wrote the angular-friendly code...
  • Christian
    Christian about 10 years
    Controllers actually should not have that kind of jquery code. Avoid accessing the DOM from there, thats why directives are there. This kind of code might break your testing code.
  • Dachande663
    Dachande663 about 10 years
    And for anywhere else I'd agree with you. But this is a browser quirk that a headless phantomjs instance wouldn't pick up anyway.
  • beauXjames
    beauXjames over 9 years
    would be nice...but nope...not consistent across the board
  • Eugene
    Eugene over 8 years
    link.apply throws error. ngModel isn't passed through since compile function doesn't accept it and therefore $setViewValue doesn't work.
  • anujeet
    anujeet over 7 years
    Hi I'm new to angularjs, after adding the above code in js file and including it in the HTML file, I'm getting this error Uncaught Error: [$injector:nomod] errors.angularjs.org/1.4.8/$injector/nomod?p0=myApp Could you please help me out?
  • Luke
    Luke over 7 years
    Sad this doesn't work because even the Chrome notes here bugs.chromium.org/p/chromium/issues/detail?id=252609 say to use autocomplete="off"
  • Luke
    Luke over 7 years
    on/off seem to be the HTML5 standard for the autocompleteproperty. w3.org/wiki/HTML/Elements/input/text
  • Alex Klaus
    Alex Klaus over 7 years
    Is it the best solution for Angular2 as well? It seems that when the user chooses a Chrome's suggestion to fill the form, the model doesn't pick up changes.
  • Campinho
    Campinho over 6 years
    I know the question was about angularjs. But why add code to fix the issue when you can add an attribute to the html?
  • andrea.rinaldi
    andrea.rinaldi over 6 years
    and why should I work on validation in order to deal with autocompletion?
  • Cory Silva
    Cory Silva over 6 years
    I should have mentioned that this was a temporary fix while the bug existed in Chrome. It is no longer valid.
  • no id
    no id over 5 years
    > Note: ngForm cannot be used as a replacement for <form>, because it lacks its built-in HTML functionality. Specifically, you cannot submit ngForm like a <form> tag. That means, you cannot send data to the server with ngForm, or integrate it with ngSubmit. docs.angularjs.org/api/ng/directive/ngForm
  • Matej Janovčík
    Matej Janovčík over 5 years
    Of course you can send data to the server with ngForm, you just need to write custom logic, as you correctly mentioned it doesn't have built-in HTML form functionality