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条回答
一纸荒年 Trace。
2楼-- · 2019-01-03 05:19

Dirty code, check if issue https://github.com/angular/angular.js/issues/1460#issuecomment-18572604 is fixed before using this code. This directive triggers events when field is filled, not only before submit (it's necessary if you have to handle input before submit)

 .directive('autoFillableField', function() {
    return {
                   restrict: "A",
                   require: "?ngModel",
                   link: function(scope, element, attrs, ngModel) {
                       setInterval(function() {
                           var prev_val = '';
                           if (!angular.isUndefined(attrs.xAutoFillPrevVal)) {
                               prev_val = attrs.xAutoFillPrevVal;
                           }
                           if (element.val()!=prev_val) {
                               if (!angular.isUndefined(ngModel)) {
                                   if (!(element.val()=='' && ngModel.$pristine)) {
                                       attrs.xAutoFillPrevVal = element.val();
                                       scope.$apply(function() {
                                           ngModel.$setViewValue(element.val());
                                       });
                                   }
                               }
                               else {
                                   element.trigger('input');
                                   element.trigger('change');
                                   element.trigger('keyup');
                                   attrs.xAutoFillPrevVal = element.val();
                               }
                           }
                       }, 300);
                   }
               };
});
查看更多
地球回转人心会变
3楼-- · 2019-01-03 05:19

If you want to keep it simple just get the value using javascript

In your angular js controller :

var username = document.getElementById('username').value;

查看更多
趁早两清
4楼-- · 2019-01-03 05:23

This is jQuery way :

$(window).load(function() {
   // updates autofilled fields
   window.setTimeout(function() {
     $('input[ng-model]').trigger('input');
   }, 100);
 });

This is Angular way :

 app.directive('autofill', ['$timeout', function ($timeout) {
    return {
        scope: true,
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl) {
            $timeout(function(){
                $(elem[0]).trigger('input');
                // elem.trigger('input'); try this if above don't work
            }, 200)
        }
    }
}]);

HTML

<input type="number" autofill /> 
查看更多
Summer. ? 凉城
5楼-- · 2019-01-03 05:23

None of these solutions worked for my use case. I have some form fields that use ng-change to watch for change. Using $watch is no help as it is not triggered by autofill. Since I have no submit button there is no easy way to run some of the solutions and I was not successful using intervals.

I ended up disabling autofill - not ideal but a lot less confusing to the user.

<input readonly onfocus="this.removeAttribute('readonly');">

Found the answer here

查看更多
等我变得足够好
6楼-- · 2019-01-03 05:25

Seems like clear straight ahead solution. No jQuery needed.

UPDATE:

  • Model is updated only when model value isn't equal to actual input value.
  • Checking doesn't stop on first autofill. In case if you wish to use another account for example.

app.directive('autofillable', ['$timeout', function ($timeout) {
    return {
        scope: true,
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl) {
            scope.check = function(){
                var val = elem[0].value;
                if(ctrl.$viewValue !== val){
                    ctrl.$setViewValue(val)
                }
                $timeout(scope.check, 300);
            };
            scope.check();
        }
    }
}]);
查看更多
霸刀☆藐视天下
7楼-- · 2019-01-03 05:26

This is the solution I ended up using in my forms.

.directive('autofillSync', [ function(){
  var link = function(scope, element, attrs, ngFormCtrl){
    element.on('submit', function(event){
      if(ngFormCtrl.$dirty){
        console.log('returning as form is dirty');
        return;
      }   
      element.find('input').each(function(index, input){
        angular.element(input).trigger('input');
      }); 
    }); 
  };  
  return {
    /* negative priority to make this post link function run first */
    priority:-1,
    link: link,
    require: 'form'
  };  
}]);

And the form's template will be

<form autofill-sync name="user.loginForm" class="login-form" novalidate ng-submit="signIn()">
    <!-- Input fields here -->
</form>

This way I was able to run any parsers/formatters I have on my ng-model and have the submit functionality transparent.

查看更多
登录 后发表回答