ngModelController $ parser関数を使用して入力値を特定のルールに準拠するように強制する場合、$ parser関数は、繰り返される「無効な」キーストロークで呼び出されないため、無効な文字が追加される可能性があります。
以下の例では、ディレクティブは数字以外のすべての文字を削除することを目的としています。 ほとんどの場合は機能しますが、同じ数字以外の文字を2回続けて入力すると(たとえば、「xx」)、単一の「x」が入力に表示されます。 「x」を3回入力すると、「x」が削除されます。
失敗したテストケースも含めました。
すべてのコードはhttp://codepen.io/visnup/pen/YXgLVq?editors=101で実行されてい
ディレクティブの使用法
<div ng-app="digits">
<input ng-model="number" placeholder="digits only" digits-only />
</div>
指令
angular
.module('digits', [])
.directive('digitsOnly', function() {
return {
require: 'ngModel',
link: function link(scope, element, attrs, ngModel) {
ngModel.$parsers.push(function(value) {
var numbers = value.replace(/\D/g, '');
element.val(numbers);
return numbers;
});
}
};
});
失敗したテストケース
describe('digits', function() {
beforeEach(angular.mock.module('digits'));
let scope, input;
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope;
input = $compile('<input ng-model="number" digits-only />')(scope);
}));
// works
it('should block non-digits', function() {
input.val('111x');
input.triggerHandler('input');
expect(input.val()).to.equal('111');
});
// doesn't work
it('should not flash incorrect input', function() {
input.val('111x');
input.triggerHandler('input');
input.val('111x');
input.triggerHandler('input');
expect(input.val()).to.equal('111');
});
});
これは以前#10700で取り上げられました。
$parsers
は、 $modelValue
として「保存」する前に$viewValue
を変換(のコピー)するためのものです(これがあなたの例です)。 実際には$viewValue
変更していません(ただし、要素の値は更新されます)。これにより、予期しない動作が発生します。
これは、例でよりよく説明されています。
$modelValue
:未定義$viewValue
:1element.val()
:1$modelValue
:1$viewValue
:1element.val()
:1$modelValue
:1$viewValue
:1xelement.val()
:1x$modelValue
:1$viewValue
:1x(パーサーfnから更新されることはありません)element.val()
:1(パーサーfnから更新)$modelValue
:1$viewValue
:1x(以前の$viewValue
も1x
だったため、変更は検出されませんelement.val()
:1x$viewValue
変更が検出されなかったため)!!!$modelValue
:1$viewValue
:1xelement.val()
:1x$modelValue
:1$viewValue
:1xxelement.val()
:1xx$modelValue
:1$viewValue
:1xxelement.val()
:1これを適切に処理するには、いくつかの「角度のある」方法があります。例:
ngModel.$parsers.push(function (value) {
var numbers = value.replace(/\D/g, '');
if (numbers !== value) {
ngModel.$setViewValue(numbers); // Update the `$viewValue`
ngModel.$render(); // Update the element's displayed value
}
return numbers;
});
すべてが期待どおりに機能しているように見えるので、これを閉じます。
@visnup 、不明な点がある場合は、
この問題は、新しい$ viewValueがコミットされていないときにキーストロークを繰り返すと発生します。 $ parsersコレクションでの$ setViewValueの推奨される使用は、一部のフォーラムで$ viewValueを直接設定することを優先して回避される場合があります。
以下も機能します。
ngModel.$viewValue = numbers; // Update the '$viewValue'
ngModel.$commitViewValue(); //update $$lastCommittedViewValue
ngModel.$render(); // Update the element's displayed value
再帰の落とし穴をまったく回避したい場合は、カプセル化に違反してもかまわないのであれば、$$ lastCommittedViewValueを直接更新できます。 残念ながら、これらの関数で無限再帰が発生すると、最大発生後もコンソールに報告されない場合があります。 テスト中にブレークポイントを設定し、再帰を確認するのに数分かかる価値があります。
@gkalpakと@snaptechの説明を組み合わせると、黄金の解決策になります:):+ 1:
最も参考になるコメント
$parsers
は、$modelValue
として「保存」する前に$viewValue
を変換(のコピー)するためのものです(これがあなたの例です)。 実際には$viewValue
変更していません(ただし、要素の値は更新されます)。これにより、予期しない動作が発生します。これは、例でよりよく説明されています。
$modelValue
:未定義$viewValue
:1element.val()
:1$modelValue
:1$viewValue
:1element.val()
:1$modelValue
:1$viewValue
:1xelement.val()
:1x$modelValue
:1$viewValue
:1x(パーサーfnから更新されることはありません)element.val()
:1(パーサーfnから更新)$modelValue
:1$viewValue
:1x(以前の$viewValue
も1x
だったため、変更は検出されませんelement.val()
:1x$viewValue
変更が検出されなかったため)!!!$modelValue
:1$viewValue
:1xelement.val()
:1x$modelValue
:1$viewValue
:1xxelement.val()
:1xx$modelValue
:1$viewValue
:1xxelement.val()
:1これを適切に処理するには、いくつかの「角度のある」方法があります。例:
更新されたペン
すべてが期待どおりに機能しているように見えるので、これを閉じます。
@visnup 、不明な点がある場合は、