Angularjs Chrome 自动完成的困境

Posted

技术标签:

【中文标题】Angularjs Chrome 自动完成的困境【英文标题】:Angularjs Chrome autocomplete dilemma 【发布时间】:2014-02-05 17:58:00 【问题描述】:

我有一个简单的登录表单,除非您使用 Chrome 的自动完成功能,否则它的工作原理很简单。

如果您开始输入并使用自动完成功能并且它会自动填充您的密码,那么我的 angularjs 模型没有任何密码值。

我尝试通过设置autocomplete="off" 表单上的属性来关闭自动完成功能,但这似乎没有任何效果。

我该怎么做: 1.如果有人使用Chrome的自动完成功能,确保我可以获得价值? 2. 禁用 Chrome 的自动完成功能?

<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>

【问题讨论】:

你知道这个帖子吗:***.com/questions/11708092/detecting-browser-autofill? 是的,我有。我正在按照他的建议设置autocomplete="off",但它不起作用。 另一个似乎可行的想法。我以两种形式将字段分开。 form1 和 form2 的密码字段不再自动填充。 有趣。绝对是让它工作的黑客,但我担心我的研究没有很好的解决方案...... 然后下一次尝试:angular 不需要表单元素。您可以使用带有 ng-form="formName" 的 div,结果相同:没有自动完成功能。 【参考方案1】:

正如这里所说,https://developer.mozilla.org/en-US/docs/Web/html/Element/form

自动完成请求的 Google Chrome 用户界面会有所不同,具体取决于 自动完成是否设置为关闭输入元素及其 形式。具体来说,当表单的自动完成设置为关闭并且其 输入元素的自动完成字段未设置,然后如果用户要求 对于输入元素的自动填充建议,Chrome 可能会显示 消息说“此表单的自动完成功能已被禁用。”在 另一方面,如果表单和输入元素都具有自动完成功能 设置为关闭,浏览器将不会显示该消息。为了这 原因,您应该将自动完成设置为关闭每个输入 自定义自动完成。

您需要在 forminput 上都设置 autocomplete="off"

我认为这与 AngularJS 无关

【讨论】:

这个问题与 AngularJS 有关,以防有解决方案来捕获 Angularjs 中的自动填充文本。我不知道必须在输入上设置自动完成功能。 我尝试在表单和输入元素上设置 autocomplete="off" 并且自动完成仍在自动完成,并且该值仍未设置为我的角度模型。 很遗憾这不起作用,因为这里的 Chrome 注释 bugs.chromium.org/p/chromium/issues/detail?id=252609 说要使用 autocomplete="off"【参考方案2】:

您可以查看电子邮件字段的值,并且每次该字段中的值发生变化时,您都可以在密码字段上触发“更改”事件。此事件触发该字段上的所有 ng-model 魔法并更新模型。

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

使用此指令,您可以像这样处理这种情况:

<input type="email" name="email" ng-model="user.email">
<input type="password" autocomplete-for="user.email" name="password" ng-model="user.password"   required>
                       -----------------------------

【讨论】:

【参考方案3】:

--- 不再相关 ---

通过添加以下内容,我能够禁用自动完成功能(很奇怪)。

<form ... novalidate>
<input ... formnovalidate />

Reference this Plunker

【讨论】:

会很好...但不...不一致 我为什么要进行验证以处理自动完成? 我应该提到这是一个临时修复,而该错误存在于 Chrome 中。它不再有效。【参考方案4】:

来自评论中添加的链接: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);
            
        
    
]);

【讨论】:

+1 这是此页面上的最佳解决方案,因为它不会禁用自动完成。禁用自动完成就像说“嘿用户,搞砸你和你的偏好”。唯一的跨浏览器解决方案是轮询。不难,而且已经有人写了对角度友好的代码…… link.apply 抛出错误。 ngModel 未通过,因为 compile 函数不接受它,因此 $setViewValue 不起作用。 嗨,我是 angularjs 新手,在 js 文件中添加上述代码并将其包含在 HTML 文件中后,我收到此错误 Uncaught Error: [$injector:nomod] errors.angularjs.org/1.4.8/$injector/nomod?p0=myApp你能帮帮我吗? 它也是 Angular2 的最佳解决方案吗?似乎当用户选择 Chrome 的建议来填写表单时,模型不会接受更改。【参考方案5】:

我遇到了同样的问题,并找到了一个非常简单的解决方案,它只使用 jQuery 来获取提交时的值。在我的控制器中,我有以下内容:

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

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

有一些缺点,如果您需要进行验证等,但对于像这样的较小表单来说,这很好。

【讨论】:

控制器实际上不应该有那种 jquery 代码。避免从那里访问 DOM,这就是存在指令的原因。这种代码可能会破坏您的测试代码。 在其他任何地方我都同意你的看法。但这是一个浏览器怪癖,无头 phantomjs 实例无论如何都不会接受。【参考方案6】:

我对 Chrome 35.0、Firefox 30.0、angular 1.2.18 的解决方案(带有密码管理器、自动填充、angular 方法和重定向的登录页面):

How does browser know when to prompt user to save password?

【讨论】:

【参考方案7】:

以下指令对我有用。这是简单而干净的修复。希望对您有所帮助!

参考:AngularJS browser autofill workaround by using a directive

这里的解决方案远没有其他解决方案那么 hacky,并且在语义上是合理的 AngularJS:VictorBlog.com

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 auto-filled 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>

【讨论】:

【参考方案8】:

要禁用输入的自动完成/自动填充,只需键入: - autocomplete="false" 而不是 autocomplete="off"!

【讨论】:

开/关似乎是autocompleteproperty 的 HTML5 标准。 w3.org/wiki/HTML/Elements/input/text【参考方案9】:

老问题,但无论如何

我遇到了同样的问题,我有一个小的“破解”解决方案。这个问题发生在我的应用程序的许多不同地方,所以我创建了一个可重用性指令。

module.directive("fakeAutocomplete", [
  function () 
    return 
      restrict: "EA",
      replace: true,
      template: "<div><input/><input type=\"password\"/></div>",
      link: function (scope, elem, attrs) 
        elem.css(
          "overflow": "hidden",
          "width": "0px",
          "height": "0px"
        );
      
    
  
]);

然后简单地添加

<fake-autocomplete></fake-autocomplete>

在表单的开头,浏览器会将虚假字段检测为应该自动完成的字段。简单地将display:none 放在字段上似乎也不再起作用了,我已经测试过了。

【讨论】:

【参考方案10】:

就我而言,我在表单和输入中设置属性 autocomplete="off"。

<form autocomplete="off">
   <input type="text" autocomplete="off">
</form>

【讨论】:

【参考方案11】:

发件人:Developer.mozilla.org docs Turning_off_form_autocompletion

如果作者想阻止密码字段的自动填充 在用户管理页面中,用户可以为其指定新密码 除了他们自己之外的其他人, autocomplete="new-password" 应该是 已指定,尽管尚未全部实施对此的支持 浏览器。

那么,是什么让它对我有用:

在密码字段中设置 autocomplete="new-password" 在用户名字段中设置 autocomplete="off"。

我希望它也对你有用:)

【讨论】:

我知道问题是关于 angularjs。但是,当您可以向 html 添加属性时,为什么还要添加代码来解决问题呢?【参考方案12】:

这可能是解决问题的更简单的方法。

    Angularjs 无法“看到”该值 通过 DOM (jQuery) 获取值,然后将其放回 Angularjs。

```

angular.module('someModule').directive('chromeAutofillHack', function()

    return 
        require: '^ngModel',
        restrict: 'A',
        priority: 500, // set higher priority then other custom directives
        link: function(scope, element, attrs , ngModelCtrl)
        
            ngModelCtrl.$parsers.unshift(function(email)
            
                if (!email)  // only do this when angular think there is no value
                    email = $(element).val();
                    ngModel.$setViewValue(email);
                
                return email;
            );
        
    ;
);

```

【讨论】:

【参考方案13】:

替代解决方案只是摆脱表单元素并使用 ng-form 代替,它会禁用所有浏览器干扰

<div ng-form="yourFormName" class="form-signin" ng-submit="login()">

【讨论】:

> 注意:ngForm 不能用作
的替代品,因为它缺少内置的 HTML 功能。具体来说,您不能像
标记那样提交 ngForm。这意味着,您不能使用 ngForm 将数据发送到服务器,或者将其与 ngSubmit 集成。 docs.angularjs.org/api/ng/directive/ngForm
当然你可以用ngForm向服务器发送数据,你只需要编写自定义逻辑,正如你正确提到的,它没有内置的HTML表单功能【参考方案14】:

我最终得到了一个我在这里还没有看到的不同解决方案。根据我的发现,在用户与页面交互之前,密码值不会暴露给模型(甚至可能是 js api)。单击登录按钮足以使值可用,并且数据绑定将足够早地成功,以便按钮上的单击处理程序从模型访问密码。因此,如果我可以检测到浏览器已自动填充,即使我的模型尚未更新,我也可以启用登录按钮。所以我写了一个简单的帮助服务来查看 Chrome 是否自动填充了任何输入:

utilApp.service('autoFillDetectionService', [function () 
    return 
        hasAutoFillInputs: function () 
            try
                return !!$(':-webkit-autofill').length;
            
            catch (x) 
                // IE gets here, it/jquery complains about an invalid pseudo-class
                return false;
            
        
    ;
]);

从登录控制器,我有一个间隔检查是否有任何输入字段被标记为自动填充,如果是,则启用登录按钮。

【讨论】:

【参考方案15】:

只需将 autocomplete="off" 替换为 autocomplete="new-password"。

【讨论】:

以上是关于Angularjs Chrome 自动完成的困境的主要内容,如果未能解决你的问题,请参考以下文章

Chrome Devtools控制台自动完成密钥快捷方式更改

jQuery 自动完成 + AngularJS 的问题

AngularJS $http.get() 在 Chrome 中最多需要 10-20 秒,在 Firefox 中运行良好

来自 $http 的 Angularjs 自动完成

AngularJS - 在谷歌自动完成中限制国家

浅谈Angular2与AngularJS的区别