Javascript AngularJS 浏览器使用指令自动填充解决方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14965968/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AngularJS browser autofill workaround by using a directive
提问by lucassp
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 $scopemodel won't be changed based on the autofill.
当在 AngularJS 中提交表单并使用浏览器记住密码功能时,在随后的登录尝试中,您让浏览器使用用户名和密码填写登录表单,$scope模型不会基于自动填充而改变。
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?
问题是ngModel.$setViewValue(element.val());不会根据element.val()返回值更改模型或视图。我怎样才能做到这一点?
回答by Ben Lesh
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:
除了像您正在尝试的某种工作之外,我不确定您在这里还能做什么。看来你是在正确的轨道上。我无法让我的浏览器尝试记住您 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>
I think you just need to simplify your approach a bit. The one thing I definitely recommend is to check ngModel.$pristineand 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.
我认为你只需要稍微简化你的方法。我绝对推荐的一件事是检查ngModel.$pristine并确保您没有覆盖一些糟糕的用户输入。另外,3 秒可能太长了。你不应该在 $timeout 中调用 $apply() ,顺便说一句,它应该自动为你排队 $digest 。
The real catch: Will your browser beat Angular to execution? What about my browser?
真正的问题:你的浏览器会在执行上击败 Angular 吗?我的浏览器呢?
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.
这可能是一场无法取胜的War,这就是 Angular(或 Knockout)无法轻易解决它的原因。无法保证指令初始执行时输入中数据的状态。甚至在 Angular 初始化的时候都没有......所以这是一个很难解决的问题。
回答by Ezekiel Victor
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/
这是一个解决方案,它远没有其他解决方案那么笨拙,并且在语义上是 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>
回答by bekos
You don't have to use a $timeoutor anything like this. You can use an event system.
您不必使用 a$timeout或类似的东西。您可以使用事件系统。
I think it's more Angularish and does not depend on jQuery or custom event catching.
我认为它更 Angularish 并且不依赖于 jQuery 或自定义事件捕获。
For example on your submit handler:
例如在您的提交处理程序上:
$scope.doLogin = function() {
$scope.$broadcast("autofill:update");
// Continue with the login.....
};
And then you can have an autofilldirective like this:
然后你可以有一个autofill这样的指令:
.directive("autofill", function () {
return {
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
scope.$on("autofill:update", function() {
ngModel.$setViewValue(element.val());
});
}
}
});
Finally, your HTML will be like:
最后,您的 HTML 将类似于:
<input type="text" name="username" ng-model="user.id" autofill="autofill"/>
回答by orszaczky
No need to hack anymore!Angular dev tboschmade a polyfill that triggers a change event when the browser changes form fields without triggering a change event:
不需要再破解了!Angular dev tbosch做了一个polyfill,当浏览器更改表单字段时触发更改事件而不触发更改事件:
https://github.com/tbosch/autofill-event
https://github.com/tbosch/autofill-event
For now they won't build this into the Angular code, as this is a bugfix for the browser, and also works without Angular (e.g. for plain jQuery apps).
现在他们不会将它构建到 Angular 代码中,因为这是浏览器的错误修复,并且在没有 Angular 的情况下也能工作(例如,对于普通的 jQuery 应用程序)。
"The polyfill will check for changes on document load and also when an input is left (only in the same form). However, you can trigger the check manually if you want to.
“polyfill 将检查文档加载时的更改以及留下输入时的更改(仅在同一表单中)。但是,如果您愿意,您可以手动触发检查。
The project has unit tests as well as semi automatic tests, so we finally have a place to collect all the different use case together with the required browser settings.
该项目有单元测试和半自动测试,所以我们终于有一个地方可以收集所有不同的用例以及所需的浏览器设置。
Please note: This polyfill works with plain AngularJS apps, with AngularJS/jQuery apps but also with plain jQuery apps that do not use Angular."
请注意:这个 polyfill 适用于普通的 AngularJS 应用程序、AngularJS/jQuery 应用程序以及不使用 Angular 的普通 jQuery 应用程序。”
It can be installed with:
它可以安装:
bower install autofill-event --save
Add the script autofill-event.jsafterjQuery or Angular in your page.
在页面中的 jQuery 或 Angularautofill-event.js之后添加脚本。
This will do the following:
这将执行以下操作:
- after DOMContentLoaded: check all input fields
- a field is left: check all other fields in the same form
- DOMContentLoaded 之后:检查所有输入字段
- 剩下一个字段:检查同一表单中的所有其他字段
API (to manually trigger the check):
API(手动触发检查):
$el.checkAndTriggerAutoFillEvent(): Execute the check for all DOM elements in the given jQuery / jQLite element.
$el.checkAndTriggerAutoFillEvent(): 对给定的 jQuery / jQLite 元素中的所有 DOM 元素执行检查。
How it works
这个怎么运作
Remember all changes to input elements by the user (listening for change events) and also by JavaScript (by intercepting $el.val() for jQuery / jQLite elements). That changed value is stored on the element in a private property.
Checking an element for auto fill: Compare the current value of the element with the remembered value. If it's different, trigger a change event.
记住用户(侦听更改事件)和 JavaScript(通过拦截 $el.val() 以获取 jQuery / jQLite 元素)对输入元素的所有更改。更改后的值存储在私有属性中的元素上。
检查元素是否自动填充:将元素的当前值与记住的值进行比较。如果不同,则触发更改事件。
Dependencies
依赖关系
AngularJS or jQuery (works with either one or both)
AngularJS 或 jQuery(适用于其中之一或两者)
More info and source on the github page.
github 页面上的更多信息和来源。
Original Angular Issue #1460 on Github can be read here.
回答by OZ_
Dirty code, check if issue https://github.com/angular/angular.js/issues/1460#issuecomment-18572604is 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)
脏代码,请在使用此代码之前检查问题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);
}
};
});
回答by gorpacrate
Seems like clear straight ahead solution. No jQuery needed.
似乎是明确的直接解决方案。不需要jQuery。
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();
}
}
}]);
回答by Robin Rizvi
Solution 1 [Using $timeout]:
解决方案 1 [使用 $timeout]:
Directive:
指示:
app.directive('autoFillSync', function($timeout) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, model) {
var origVal = elem.val();
$timeout(function () {
var newVal = elem.val();
if(model.$pristine && origVal !== newVal) {
model.$setViewValue(newVal);
}
}, 500);
}
};
});
HTML:
HTML:
<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>
Solution 2 [Using angular events]:
解决方案2【使用角度事件】:
Ref: Becko's answer
参考:Becko 的回答
Directive:
指示:
app.directive("autofill", function () {
return {
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
scope.$on("autofill:update", function() {
ngModel.$setViewValue(element.val());
});
}
};
});
HTML:
HTML:
<form name="myForm" ng-submit="login()">
<label for="username">Username</label>
<input type="text" id="username" name="username" ng-model="username" autofill/><br/>
<label for="password">Password</label>
<input type="password" id="password" name="password" ng-model="password" autofill/><br/>
<button type="submit">Login</button>
</form>
Solution 3 [Using relay method calls]:
方案三【使用中继方法调用】:
Directive:
指示:
app.directive('autoFill', function() {
return {
restrict: 'A',
link: function(scope,element) {
scope.submit = function(){
scope.username = element.find("#username").val();
scope.password = element.find("#password").val();
scope.login();//call a login method in your controller or write the code here itself
}
}
};
});
HTML:
HTML:
<form name="myForm" auto-fill ng-submit="submit()">
<label for="username">Username</label>
<input type="text" id="username" name="username" ng-model="username" />
<label for="password">Password</label>
<input type="password" id="password" name="password" ng-model="password" />
<button type="submit">Login</button>
</form>
回答by pedroassis
Well, the easiest way it's to emulate the browser's behavior, so if there is a problem with the change event, just fire it yourself. Much simpler.
嗯,最简单的方法是模拟浏览器的行为,所以如果 change 事件有问题,只需自己触发它。简单多了。
Directive:
指示:
yourModule.directive('triggerChange', function($sniffer) {
return {
link : function(scope, elem, attrs) {
elem.on('click', function(){
$(attrs.triggerChange).trigger(
$sniffer.hasEvent('input') ? 'input' : 'change'
);
});
},
priority : 1
}
});
HTML:
HTML:
<form >
<input data-ng-model="user.nome" type="text" id="username">
<input data-ng-model="user.senha" type="password" id="password" >
<input type="submit" data-ng-click="login.connect()" id="btnlogin"
data-trigger-change="#password,#username"/>
</form>
You can do some variations, like putting the directive on the form and firing the event on all inputs with the .dirty class on form submit.
你可以做一些变化,比如将指令放在表单上,并在表单提交时使用 .dirty 类在所有输入上触发事件。
回答by Nishchit Dhanani
This is jQuery way :
这是 jQuery 方式:
$(window).load(function() {
// updates autofilled fields
window.setTimeout(function() {
$('input[ng-model]').trigger('input');
}, 100);
});
This is Angular way :
这是 Angular 方式:
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
HTML
<input type="number" autofill />
回答by Swaron Chen
Changing the model value, instead of using a timeout function worked for me.
更改模型值,而不是使用超时函数对我有用。
Here is my code:
这是我的代码:
module.directive('autoFill', [ function() {
return {
require: 'ngModel',
link:function(scope, element, attr, ngModel) {
var origVal = element.val();
if(origVal){
ngModel.$modelValue = ngModel.$modelValue || origVal;
}
}
};
}]);

