在AngularJS中提交表单并使用浏览器记住密码功能时,在随后的登录尝试中,您让浏览器使用用户名和密码填写登录表单,$scope
模型赢了&t根据自动填充进行更改。
我发现的唯一脏黑客是使用以下指令:
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()
返回的值更改模型和视图。我怎么能做到这一点?
答案 0 :(得分:45)
Apparently this is a known issue with Angular and is currently open
除了你正在尝试的某种类似的工作之外,我不确定你能做什么。看来你走在正确的轨道上。我无法让我的浏览器尝试记住你的插件的密码,所以我不确定这是否会起作用但看看:
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,BTW中调用$ apply(),它应该自动为你排队$。
真正的问题:您的浏览器是否会击败Angular执行?我的浏览器怎么样?
这可能是一场无法取胜的战争,这就是Angular(或Knockout)无法轻易解决的原因。在指令初始执行时,无法保证输入中的数据状态。甚至在Angular初始化的时候......所以这是一个棘手的问题需要解决。
答案 1 :(得分:35)
您不必使用$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"/>
答案 2 :(得分:35)
这是一个远比其他解决方案更难以解决的解决方案,语义上是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>
答案 3 :(得分:10)
脏代码,在使用此代码之前检查问题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);
}
};
});
答案 4 :(得分:10)
不再需要破解了! 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或Angular之后添加脚本autofill-event.js
。
这将执行以下操作:
API(手动触发检查):
$el.checkAndTriggerAutoFillEvent()
:对给定的jQuery / jQLite元素中的所有DOM元素执行检查。记住用户对所有输入元素的更改(侦听更改事件)以及JavaScript(通过拦截jQuery / jQLite元素的$ el.val())。该更改的值存储在私有属性中的元素上。
检查元素是否自动填充:将元素的当前值与记忆值进行比较。如果不同,则触发更改事件。
AngularJS或jQuery(与其中一个或两个一起工作)
github page上的更多信息和来源。
Github can be read here.上的原始角度问题#1460
答案 5 :(得分:5)
似乎是明确的直接解决方案。不需要jQuery。
<强>更新强>
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();
}
}
}]);
答案 6 :(得分:4)
嗯,这是模拟浏览器行为的最简单方法,因此如果更改事件出现问题,请自行启动。更简单。
指令:
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:
<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>
您可以执行一些变体操作,例如将指令放在表单上,并在表单提交上使用.dirty类在所有输入上触发事件。
答案 7 :(得分:4)
解决方案1 [使用$ timeout]:
指令:
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:
<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>
解决方案2 [使用角度事件]:
指令:
app.directive("autofill", function () {
return {
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
scope.$on("autofill:update", function() {
ngModel.$setViewValue(element.val());
});
}
};
});
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>
解决方案3 [使用中继方法调用]:
指令:
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:
<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>
答案 8 :(得分:3)
这是jQuery方式:
$(window).load(function() {
// updates autofilled fields
window.setTimeout(function() {
$('input[ng-model]').trigger('input');
}, 100);
});
这是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 强>
<input type="number" autofill />
答案 9 :(得分:1)
这是另一种不太苛刻的解决方法,但在控制器中需要一些额外的代码。
HTML:
<form ng-submit="submitForm()" ng-controller="FormController">
<input type="text" ng-model="username" autocomplete-username>
<input type="submit">
</form>
指令(CoffeeScript):
directives.directive 'autocompleteUsername', ->
return (scope, element) ->
scope.getUsername = ->
element.val()
控制器:
controllers.controller 'FormController', [->
$scope.submitForm = ->
username = $scope.getUsername?() ? $scope.username
# HTTP stuff...
]
答案 10 :(得分:1)
提交处理程序中的单行解决方法(需要jQuery):
if (!$scope.model) $scope.model = $('#input_field').val();
答案 11 :(得分:1)
更改模型值,而不是使用超时功能为我工作。
这是我的代码:
module.directive('autoFill', [ function() {
return {
require: 'ngModel',
link:function(scope, element, attr, ngModel) {
var origVal = element.val();
if(origVal){
ngModel.$modelValue = ngModel.$modelValue || origVal;
}
}
};
}]);
答案 12 :(得分:1)
这是我发现的唯一允许我的所有Angular验证按设计工作的解决方案,包括禁用/启用提交按钮。使用bower和1个脚本标记安装。 Bazinga!
答案 13 :(得分:0)
这些解决方案都不适用于我的用例。我有一些使用ng-change来监视变化的表单字段。使用$watch
没有任何帮助,因为它不是由自动填充触发的。由于我没有提交按钮,因此没有简单的方法来运行某些解决方案,并且我没有成功使用间隔。
我最终禁用了自动填充功能 - 这并不理想,但却让用户感到困惑。
<input readonly onfocus="this.removeAttribute('readonly');">
找到答案here
答案 14 :(得分:0)
这是一个简单的修复程序,适用于我在Firefox和Chrome中测试的所有情况。请注意,使用最高答案(指令w /超时)我遇到了问题 -
这个解决方案显然非常愚蠢和hacky,但它在100%的时间都有效 -
function myScope($scope, $timeout) {
// ...
(function autoFillFix() {
$timeout(function() {
$('#username').trigger('change');
$('#password').trigger('change');
autoFillFix(); }, 500);
})();
}
答案 15 :(得分:0)
没有指令的解决方案:
.run(["$window", "$rootElement", "$timeout", function($window, $rootElement, $timeout){
var event =$window.document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
$timeout(function(){
Array.apply(null, $rootElement.find("input")).forEach(function(item){
if (item.value.length) {
item.$$currentValue = item.value;
item.dispatchEvent(event);
}
});
}, 500);
}])
答案 16 :(得分:0)
这是我最终在表格中使用的解决方案。
.directive('autofillSync', [ function(){
var link = function(scope, element, attrs, ngFormCtrl){
element.on('submit', function(event){
if(ngFormCtrl.$dirty){
console.log('returning as form is dirty');
return;
}
element.find('input').each(function(index, input){
angular.element(input).trigger('input');
});
});
};
return {
/* negative priority to make this post link function run first */
priority:-1,
link: link,
require: 'form'
};
}]);
表单的模板将是
<form autofill-sync name="user.loginForm" class="login-form" novalidate ng-submit="signIn()">
<!-- Input fields here -->
</form>
通过这种方式,我可以在ng-model上运行任何解析器/格式化程序,并使提交功能透明。
答案 17 :(得分:0)
对此答案进行微小修改(https://stackoverflow.com/a/14966711/3443828):使用$ interval而不是$ timeout,这样您就不必在浏览器中竞争。
mod.directive('autoFillSync', function($interval) {
function link(scope, element, attrs, ngModel) {
var origVal = element.val();
var refresh = $interval(function() {
if (!ngModel.$pristine) {
$interval.cancel(refresh);
}else{
var newVal = element.val();
if (origVal !== newVal) {
ngModel.$setViewValue(newVal);
$interval.cancel(refresh);
}
}
}, 100);
}
return {
require: 'ngModel',
link: link
}
});
答案 18 :(得分:0)
您可以尝试以下代码:
yourapp.directive('autofill',function () {
return {
scope: true,
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var origVal = elem.val();
if (origVal != '') {
elem.trigger('input');
}
}
}
});
答案 19 :(得分:0)
我对Angularjs很新,但我发现了一个解决这个问题的简单方法=&gt; 强制Angular重新评估表达...通过改变它! (当然你需要记住恢复到初始状态的初始值) 以下是控制器功能提交表单的方式:
$scope.submit = function () {
var oldpassword = $scope.password;
$scope.password = '';
$scope.password = oldpassword;
//rest of your code of the submit function goes here...
当然,密码输入中输入的值是由Windows设置的,而不是由用户设置的。
答案 20 :(得分:0)
我在提交时强制使用$ setValue(val()):(这可以 没有 jQuery)
var ValidSubmit = ['$parse', function ($parse) {
return {
compile: function compile(tElement, tAttrs, transclude) {
return {
post: function postLink(scope, element, iAttrs, controller) {
var form = element.controller('form');
form.$submitted = false;
var fn = $parse(iAttrs.validSubmit);
element.on('submit', function(event) {
scope.$apply(function() {
var inputs = element.find('input');
for(var i=0; i < inputs.length; i++) {
var ele = inputs.eq(i);
var field = form[inputs[i].name];
field.$setViewValue(ele.val());
}
element.addClass('ng-submitted');
form.$submitted = true;
if(form.$valid) {
fn(scope, {$event:event});
}
});
});
scope.$watch(function() { return form.$valid}, function(isValid) {
if(form.$submitted == false) return;
if(isValid) {
element.removeClass('has-error').addClass('has-success');
} else {
element.removeClass('has-success');
element.addClass('has-error');
}
});
}
}
}
}
}]
app.directive('validSubmit', ValidSubmit);
答案 21 :(得分:-1)
如果您使用的是jQuery,可以在表单提交上执行此操作:
HTML:
<form ng-submit="submit()">
<input id="email" ng-model="password" required
type="text" placeholder="Your email">
<input id="password" ng-model="password" required
type="password" placeholder="Password">
</form>
JS:
$scope.submit = function() {
$scope.password = $('#password').val();
}
答案 22 :(得分:-1)
如果你想保持简单,只需使用javascript获取值
在你的角度js控制器中:
var username = document.getElementById(&#39; username&#39;)。value;