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()
の戻り値に基づいてモデルやビューを変更しないことです。どうすればそれを達成できますか?
明らかに、これはAngularの既知の問題であり、現在オープンです
あなたがしようとしているような回避策のほかに、ここで何ができるのか分かりません。あなたは正しい軌道に乗っているようです。私のブラウザにあなたのプランクのパスワードを記憶させようとすることができなかったので、これがうまくいくかどうかはわかりませんが、見てください:
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の初期化時でさえも…。だから、それは解決するのが難しい問題です。
以下は、提示されている他のソリューションよりもはるかにハッキングが少なく、意味的に健全な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>
$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"/>
もうハッキングする必要はありません!Angular devtbosch変更イベントをトリガーせずにブラウザがフォームフィールドを変更したときに変更イベントをトリガーするポリフィルを作成しました:
https://github.com/tbosch/autofill-event
現時点では、これはAngularコードに組み込まれません。これはブラウザーのバグ修正であり、Angularなしでも機能するためです(例:jQueryアプリの場合)。
「ポリフィルは、ドキュメントの読み込みの変更をチェックし、入力が(同じフォームでのみ)残されたときにもチェックします。ただし、必要に応じて手動でチェックをトリガーできます。
プロジェクトには、半自動テストだけでなくユニットテストも含まれているため、最終的には、必要なブラウザ設定とともにすべての異なるユースケースを収集する場所ができました。
注:このポリフィルは、AngularJSアプリだけでなく、AngularJS/jQueryアプリでも動作しますが、Angularを使用しないjQueryアプリでも動作します。
以下を使用してインストールできます。
bower install autofill-event --save
スクリプトautofill-event.js
afterjQueryまたはAngularをページに追加します。
これにより、次のことが行われます。
API(チェックを手動でトリガーするため):
$el.checkAndTriggerAutoFillEvent()
:指定されたjQuery/jQLite要素内のすべてのDOM要素のチェックを実行します。入力要素へのすべての変更を、ユーザー(変更イベントのリスニング)およびJavaScript(jQuery/jQLite要素の$ el.val()をインターセプトすること)によって記憶します。その変更された値は、プライベートプロパティの要素に保存されます。
要素の自動入力の確認:要素の現在の値と記憶されている値を比較します。異なる場合は、変更イベントをトリガーします。
AngularJSまたはjQuery(いずれかまたは両方で動作)
githubページ の詳細とソース。
オリジナルAngular Githubの問題#1460 ここで読むことができます
コードが汚れています。問題を確認してください 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);
}
};
});
明確で直進的なソリューションのようです。 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();
}
}
}]);
ソリューション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 [angularイベントの使用]:
参照: Beckoの答え
ディレクティブ:
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>
さて、ブラウザの動作をエミュレートするのが最も簡単な方法です。したがって、changeイベントに問題がある場合は、自分で起動してください。はるかに簡単です。
指令:
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クラスを使用してすべての入力でイベントを発生させるなど、いくつかのバリエーションを実行できます。
これはjQueryの方法です:
$(window).load(function() {
// updates autofilled fields
window.setTimeout(function() {
$('input[ng-model]').trigger('input');
}, 100);
});
これはAngular wayです:
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 />
ハッキングは少ないが、コントローラーに追加のコードが必要な別の回避策があります。
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...
]
これは、送信ボタンの無効化/有効化を含む設計どおりにすべてのAngularの検証を機能させることができる唯一のソリューションです。 bowerと1つのscriptタグでインストールします。バジンガ!
送信ハンドラーでの1行の回避策(jQueryが必要):
if (!$scope.model) $scope.model = $('#input_field').val();
タイムアウト機能を使用する代わりに、モデルの値を変更することでうまくいきました。
ここに私のコードがあります:
module.directive('autoFill', [ function() {
return {
require: 'ngModel',
link:function(scope, element, attr, ngModel) {
var origVal = element.val();
if(origVal){
ngModel.$modelValue = ngModel.$modelValue || origVal;
}
}
};
}]);
私は送信時に$ setValue(val())を強制します:(これはwithout 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);
これは、FirefoxとChromeの両方でテストしたすべてのケースで機能する簡単な修正です。一番上の回答(タイムアウト付きのディレクティブ)で問題が発生したことに注意してください-
この修正は明らかに非常に馬鹿げてハックですが、100%の時間で動作します-
function myScope($scope, $timeout) {
// ...
(function autoFillFix() {
$timeout(function() {
$('#username').trigger('change');
$('#password').trigger('change');
autoFillFix(); }, 500);
})();
}
この回答に対するマイナーな変更( https://stackoverflow.com/a/14966711/3443828 ):$ timeoutの代わりに$ intervalを使用して、ブラウザと競合する必要がないようにします。
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
}
});
このコードを試すことができます:
yourapp.directive('autofill',function () {
return {
scope: true,
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var origVal = elem.val();
if (origVal != '') {
elem.trigger('input');
}
}
}
});
私はAngularjsは初めてですが、その問題の簡単な解決策を見つけました=>強制的にAngularを変更して式を再評価します! (もちろん、初期状態に戻すには初期値を覚えておく必要があります)フォームを送信するためのコントローラー関数での方法は次のとおりです。
$scope.submit = function () {
var oldpassword = $scope.password;
$scope.password = '';
$scope.password = oldpassword;
//rest of your code of the submit function goes here...
もちろん、パスワード入力に入力された値は、ユーザーではなくWindowsによって設定されています。
これがフォームで使用することになったソリューションです。
.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モデルでパーサー/フォーマッターを実行し、送信機能を透過的にすることができました。
これらのソリューションはいずれも私のユースケースでは機能しませんでした。 ng-changeを使用して変更を監視するフォームフィールドがいくつかあります。 $watch
の使用は、自動入力によってトリガーされないため、役に立ちません。送信ボタンがないため、ソリューションのいくつかを実行する簡単な方法はなく、間隔を使用して成功しませんでした。
最終的には自動入力を無効にしました-理想的ではありませんが、ユーザーの混乱はずっと少なくなりました。
<input readonly onfocus="this.removeAttribute('readonly');">
答えを見つけました こちら
ディレクティブなしのソリューション:
.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);
}])