AngularJS 浏览器使用指令自动填充解决方法

IT技术 javascript mvvm angularjs
2021-02-01 00:02:22

当在 AngularJS 中提交表单并使用浏览器记住密码功能时,在随后的登录尝试中,您让浏览器使用用户名和密码填写登录表单,$scope模型不会基于自动填充而改变。

我发现的唯一肮脏的黑客是使用以下指令:

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);
        }
    };
}]);

问题是ngModel.$setViewValue(element.val());不会根据element.val()返回值更改模型或视图我怎样才能做到这一点?

6个回答

显然,这是 Angular 的一个已知问题,目前已开放

除了像您正在尝试的某种工作之外,我不确定您在这里还能做什么。看来你是在正确的轨道上。我无法让我的浏览器尝试记住您 plunk 的密码,所以我不确定这是否可行,但请看一下:

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>

我认为你只需要稍微简化你的方法。我绝对推荐的一件事是检查ngModel.$pristine并确保您没有覆盖一些糟糕的用户输入。另外,3 秒可能太长了。你不应该在 $timeout 中调用 $apply() ,顺便说一句,它应该自动为你排队 $digest 。

真正的问题:你的浏览器会在执行上击败 Angular 吗?我的浏览器呢?

这可能是一场无法取胜的战争,这就是 Angular(或 Knockout)无法轻易解决它的原因。无法保证指令初始执行时输入中数据的状态。甚至在 Angular 初始化的时候也没有......所以这是一个棘手的问题。

我在开玩笑 localStorage,你不会想在那里保留密码。
2021-03-20 00:02:22
用本地存储欺骗他。:) ...我会继续考虑的。但是我对使用任何双向绑定进行自动填充的可行性表示怀疑。
2021-03-21 00:02:22
是的,但我仍然需要能够从指令更新模型才能使其工作。
2021-03-31 00:02:22
这不适用于 Safari 和信用卡自动填充,因为此自动填充是由用户随时触发的
2021-04-04 00:02:22
关于 $pristine 的好点子。嗯,3 秒只是为了测试目的。“保存密码”对话框似乎适用于 Safari。这是我必须调查的另一个问题。
2021-04-11 00:02:22

这是一个解决方案,它远没有其他解决方案那么笨拙,并且在语义上是 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);
    }
  };
});

然后您只需将指令附加到您的表单中:

<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>
由于这使用了 jQuery,因此没有 jQuery 预计它不会工作。
2021-03-27 00:02:22
现在是 2018 年,这仍然是解决方案。谢谢以西结!
2021-04-01 00:02:22
这个有效。我们尝试了上述大部分方法,但没有任何结果。谢谢!!!结果在mobbr.com 上
2021-04-05 00:02:22

您不必使用 a$timeout或类似的东西。您可以使用事件系统。

我认为它更 Angularish 并且不依赖于 jQuery 或自定义事件捕获。

例如在您的提交处理程序上:

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

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

然后你可以有一个autofill这样指令:

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

最后,您的 HTML 将类似于:

<input type="text" name="username" ng-model="user.id" autofill="autofill"/>
在我的情况下,当输入为空时提交按钮被禁用。
2021-04-04 00:02:22
因为它是异步的,所以你从来没有遇到过这个问题吗?在复制事件之前对服务器的登录调用已经离开并且指令有时间更新范围?
2021-04-06 00:02:22

不需要再破解了!Angular dev tbosch做了一个polyfill,当浏览器更改表单字段时触发更改事件而不触发更改事件:

https://github.com/tbosch/autofill-event

目前他们不会将其构建到 Angular 代码中,因为这是浏览器的错误修复,并且在没有 Angular 的情况下也能工作(例如,对于普通的 jQuery 应用程序)。

“polyfill 将检查文档加载时的更改以及保留输入时的更改(仅在同一表单中)。但是,如果您愿意,您可以手动触发检查。

该项目有单元测试和半自动测试,所以我们终于有一个地方可以收集所有不同的用例以及所需的浏览器设置。

请注意:这个 polyfill 适用于普通的 AngularJS 应用程序、AngularJS/jQuery 应用程序以及不使用 Angular 的普通 jQuery 应用程序。”

它可以通过以下方式安装:

bower install autofill-event --save

在页面中的 jQuery 或 Angularautofill-event.js 之后添加脚本

这将执行以下操作:

  • DOMContentLoaded 之后:检查所有输入字段
  • 剩下一个字段:检查同一表单中的所有其他字段

API(手动触发检查):

  • $el.checkAndTriggerAutoFillEvent(): 对给定的 jQuery / jQLite 元素中的所有 DOM 元素执行检查。

怎么运行的

  1. 记住用户(侦听更改事件)和 JavaScript(通过拦截 $el.val() 以获取 jQuery / jQLite 元素)对输入元素的所有更改。更改后的值存储在私有属性中的元素上。

  2. 检查元素是否自动填充:将元素的当前值与记住的值进行比较。如果不同,则触发更改事件。

依赖关系

AngularJS 或 jQuery(适用于其中之一或两者)

github 页面上的更多信息和来源

可以在此处阅读Github 上的原始 Angular Issue #1460

ngModelOptions结合autofill-event意味着您现在可以将其指定changeupdateOn事件之一,以确保您的模型正确同步。
2021-03-25 00:02:22
这对我的情况不起作用。checkAndTriggerAutoFillEvent() 被触发并生成事件,但 AngularJS 从不更新其模型并认为这些字段为空。
2021-03-31 00:02:22
我在使用这个 polyill 时没有遇到任何问题。如果你不能让它正常工作,你应该创建一个单独的问题,包括一些代码。如果你发现了一个bug,你应该检查github上的票,或者开一个新的。
2021-04-05 00:02:22
是的,我认为它与此错误有关:github.com/tbosch/autofill-event/issues/11
2021-04-06 00:02:22

脏代码,请在使用此代码之前检查问题https://github.com/angular/angular.js/issues/1460#issuecomment-18572604是否已修复。该指令在字段被填充时触发事件,而不仅仅是在提交之前(如果您必须在提交之前处理输入,这是必要的)

 .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);
                   }
               };
});