当使用 ngModelController $parser 函数强制输入值符合某些规则时,不会在重复的“无效”击键时调用 $parser 函数,这允许添加无效字符。
在下面的示例中,该指令旨在删除所有非数字字符。 它大多有效,但如果您连续输入两次相同的非数字字符(例如“xx”),则输入中将显示单个“x”。 如果您第三次键入“x”,它将删除“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
用于在将$viewValue
“保存”为$modelValue
之前对其进行转换(副本)(这就是您的示例所做的)。 它实际上并没有改变$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
用于在将$viewValue
“保存”为$modelValue
之前对其进行转换(副本)(这就是您的示例所做的)。 它实际上并没有改变$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 ,如果有任何不清楚的