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 可能会显示 消息说“此表单的自动完成功能已被禁用。”在 另一方面,如果表单和输入元素都具有自动完成功能 设置为关闭,浏览器将不会显示该消息。为了这 原因,您应该将自动完成设置为关闭每个输入 自定义自动完成。
您需要在 form
和 input
上都设置 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"!
【讨论】:
开/关似乎是autocomplete
property 的 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 不能用作 当然你可以用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控制台自动完成密钥快捷方式更改
AngularJS $http.get() 在 Chrome 中最多需要 10-20 秒,在 Firefox 中运行良好