AngularJS browser autofill workaround by using a d

2019-01-03 04:40发布

When submitting a form in AngularJS and use the browser remember password functionality, and in a subsequent login attempt you let the browser fill in the login form with the username and password, the $scope model won't be changed based on the autofill.

The only dirty hack I found is to use the following directive:

app.directive("xsInputSync", ["$timeout" , function($timeout) {
    return {
        restrict : "A",
        require: "?ngModel",
        link : function(scope, element, attrs, ngModel) {
            $timeout(function() {
                if (ngModel.$viewValue && ngModel.$viewValue !== element.val()) {
                    scope.apply(function() {
                        ngModel.$setViewValue(element.val());
                    });
                }
                console.log(scope);
                console.log(ngModel.$name);
                console.log(scope[ngModel.$name]);
            }, 3000);
        }
    };
}]);

The problem is that the ngModel.$setViewValue(element.val()); doesn't change the model nor the view based on the element.val() returned value. How can I accomplish that?

23条回答
甜甜的少女心
2楼-- · 2019-01-03 05:12

This is a simple fix that works for all the cases I've tested in both Firefox and Chrome. Note that with the top answer (directive w/ timeout) I had issues with -

  • Browser back / forward buttons, don't re-fire page load events (so the fix doesn't apply)
  • Loading of credentials some time after page load. e.g. in Firefox, double click on the login box and select from stored credentials.
  • Need a solution that updates before form submission since I disable the Login button until valid input provided

This fix is obviously very dumb and hacky, but it works 100% of the time -

function myScope($scope, $timeout) {
    // ...
    (function autoFillFix() {
        $timeout(function() { 
            $('#username').trigger('change'); 
            $('#password').trigger('change'); 
            autoFillFix(); }, 500);                    
    })();
}
查看更多
小情绪 Triste *
3楼-- · 2019-01-03 05:14

I am very new to Angularjs, but I found a simple solution to that problem=> Force Angular to reevaluate expression... by changing it! (of course you need to remember the initial value to revert to initial state) Here is the way it goes in your controller function for submitting the form:

    $scope.submit = function () {
                var oldpassword = $scope.password;
                $scope.password = '';
                $scope.password = oldpassword;
//rest of your code of the submit function goes here...

where of course, the value entered in your password input has been set by windows and not by user.

查看更多
不美不萌又怎样
4楼-- · 2019-01-03 05:14

If you are using jQuery you could do this on form submit:

HTML:

<form ng-submit="submit()">
    <input id="email" ng-model="password" required 
           type="text" placeholder="Your email">
    <input id="password" ng-model="password" required 
           type="password" placeholder="Password">
</form>

JS:

 $scope.submit = function() {
     $scope.password = $('#password').val();
}
查看更多
迷人小祖宗
5楼-- · 2019-01-03 05:16

One-liner workaround in the submit handler (requires jQuery):

if (!$scope.model) $scope.model = $('#input_field').val();
查看更多
forever°为你锁心
6楼-- · 2019-01-03 05:17

I force a $setValue(val()) on submit: (this works without jQuery)

   var ValidSubmit = ['$parse', function ($parse) {
    return {
        compile: function compile(tElement, tAttrs, transclude) {
            return {
                post: function postLink(scope, element, iAttrs, controller) {
                    var form = element.controller('form');
                    form.$submitted = false;
                    var fn = $parse(iAttrs.validSubmit);
                    element.on('submit', function(event) {
                        scope.$apply(function() {
                            var inputs = element.find('input');
                            for(var i=0; i < inputs.length; i++) {
                                var ele = inputs.eq(i);
                                var field = form[inputs[i].name];
                                field.$setViewValue(ele.val());
                            }
                            element.addClass('ng-submitted');
                            form.$submitted = true;
                            if(form.$valid) {
                                fn(scope, {$event:event});
                            }
                        });
                    });
                    scope.$watch(function() { return form.$valid}, function(isValid) {
                        if(form.$submitted == false) return;
                        if(isValid) {
                            element.removeClass('has-error').addClass('has-success');
                        } else {
                            element.removeClass('has-success');
                            element.addClass('has-error');
                        }
                    });
                }
            }
        }
    }
}]
app.directive('validSubmit', ValidSubmit);
查看更多
放我归山
7楼-- · 2019-01-03 05:18

You don't have to use a $timeout or anything like this. You can use an event system.

I think it's more Angularish and does not depend on jQuery or custom event catching.

For example on your submit handler:

$scope.doLogin = function() {
    $scope.$broadcast("autofill:update");

    // Continue with the login.....
};

And then you can have an autofill directive like this:

.directive("autofill", function () {
    return {
        require: "ngModel",
        link: function (scope, element, attrs, ngModel) {
            scope.$on("autofill:update", function() {
                ngModel.$setViewValue(element.val());
            });
        }
    }
});

Finally, your HTML will be like:

<input type="text" name="username" ng-model="user.id" autofill="autofill"/>
查看更多
登录 后发表回答