Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS browser autofill workaround by using a directive

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?

like image 975
lucassp Avatar asked Feb 19 '13 19:02

lucassp


2 Answers

Apparently this is a known issue with Angular and is currently open

I'm not sure what you could do here besides some sort of work around like you're trying. It seems you're on the right track. I couldn't get my browser to try to remember a password for your plunk, so I'm not sure if this will work but have a look:

app.directive('autoFillSync', function($timeout) {    return {       require: 'ngModel',       link: function(scope, elem, attrs, ngModel) {           var origVal = elem.val();           $timeout(function () {               var newVal = elem.val();               if(ngModel.$pristine && origVal !== newVal) {                   ngModel.$setViewValue(newVal);               }           }, 500);       }    } }); 
<form name="myForm" ng-submit="login()">    <label for="username">Username</label>    <input type="text" id="username" name="username" ng-model="username" auto-fill-sync/><br/>    <label for="password">Password</label>    <input type="password" id="password" name="password" ng-model="password" auto-fill-sync/><br/>    <button type="submit">Login</button> </form> 

I think you just need to simplify your approach a bit. The one thing I definitely recommend is to check ngModel.$pristine and make sure you're not overwriting some poor user's input. Also, 3 seconds is probably too long. You shouldn't have to call $apply() in a $timeout, BTW, it should queue a $digest for you automatically.

The real catch: Will your browser beat Angular to execution? What about my browser?

This is probably an unwinnable war, which is why Angular (or Knockout) hasn't been able to solve it readily. There's no guarantee of the state of the data in your input at the time of the directive's initial execution. Not even at the time of Angular's initialization.... So it's a tricky problem to solve.

like image 161
Ben Lesh Avatar answered Sep 19 '22 08:09

Ben Lesh


Here is a solution that is far less hacky than other solutions presented and is semantically sound AngularJS: http://victorblog.com/2014/01/12/fixing-autocomplete-autofill-on-angularjs-form-submit/

myApp.directive('formAutofillFix', function() {   return function(scope, elem, attrs) {     // Fixes Chrome bug: https://groups.google.com/forum/#!topic/angular/6NlucSskQjY     elem.prop('method', 'POST');      // Fix autofill issues where Angular doesn't know about autofilled inputs     if(attrs.ngSubmit) {       setTimeout(function() {         elem.unbind('submit').submit(function(e) {           e.preventDefault();           elem.find('input, textarea, select').trigger('input').trigger('change').trigger('keydown');           scope.$apply(attrs.ngSubmit);         });       }, 0);     }   }; }); 

Then you simply attach the directive to your form:

<form ng-submit="submitLoginForm()" form-autofill-fix>   <div>     <input type="email" ng-model="email" ng-required />     <input type="password" ng-model="password" ng-required />     <button type="submit">Log In</button>   </div> </form> 
like image 23
Ezekiel Victor Avatar answered Sep 18 '22 08:09

Ezekiel Victor