我正在尝试解决使用自定义Angular指令和Chrome自动填充时我注意到的错误。我已经为电话号码字段写了一个指令。该指令添加了破折号" - "到电话号码字段(因此用户在输入号码时不必添加任何破折号)。
该指令列于下面
app.directive("phoneNumberValidator", function () {
return {
require: "ngModel",
restrict: "A",
link: function (scope, elem, attrs, ctrl) {
var domElement = elem[0]; // Get DOM element
var phoneNumberRegex = new RegExp("\\d{3}\\-\\d{3}\\-\\d{4}"); // Phone number regex
var cursorIndex; // Index where the cursor should be
// Create a parser to alter and validate if our
// value is a valid phone number
ctrl.$parsers.push(function (value) {
// If our value is non-existent, we return undefined
// WHY?: an angular model value should be undefined if it is empty
if (typeof value === "undefined" || value === null || value == "") {
ctrl.$setValidity('invalidFormat', true); // No invalid format if the value of the phone number is empty
return undefined;
}
// PARSER LOGIC
// =compare our value to a modified value after it has
// been transformed into a "nice" phone number. If these
// values are different, we set the viewValue to
// the "nice" phone number. If these values are the same,
// we render the viewValue (aka. "nice" phone number)
var prevValue, nextValue;
prevValue = value;
nextValue = value.replace(/[\D]/gi, ""); // Strip all non-digits
// Make the "nice" phone number
if (nextValue.length >= 4 && nextValue.length <= 6) {
nextValue = nextValue.replace(/(\d{3})(\d{3})?/, "$1-$2");
} else if (nextValue.length >= 7 && nextValue.length <= 10) {
nextValue = nextValue.replace(/(\d{3})(\d{3})(\d{4})?/, "$1-$2-$3");
}
// Save the correct index where the custor should be
// WHY?: we do this here because "ctrl.$render()" shifts
// the cursor index to the end of the phone number
cursorIndex = domElement.selectionStart;
if (prevValue != nextValue) {
ctrl.$setViewValue(nextValue); // *Calling this function will run all functions in ctrl.$parsers!
} else {
ctrl.$render(); // Render the new, "nice" phone number
}
// If our cursor lands on an index where a dash "-" is,
// move it up by one
if (cursorIndex == 4 || cursorIndex == 8) {
cursorIndex = cursorIndex + 1;
}
var valid = phoneNumberRegex.test(value); // Test the validity of our phone number
ctrl.$setValidity('invalidFormat', valid); // Set the validity of the phone number field
domElement.setSelectionRange(cursorIndex, cursorIndex); // Assign the cursor to the correct index
return value; // Return the updated value
});
}
}
});
当Chrome自动填充此字段时,问题就在于此。通过使用Batarang扩展(https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en),我能够看到我的页面上使用的范围值。
当我使用Chrome 自动填充我的电话号码字段,值为&#34; 1234567899&#34;时,我的$scope
中的Angular电话字段值是1234567899(应该是:&#34; 123-546-7899&#34;)。
在我的指令中放置断点将使我能够在Chrome自动填充我的字段时看到该指令确实在我的浏览器中运行,但$scope.PhoneNumber
字段值为1234567899,而不是123-456-7899。 简而言之,$ viewModel在自动填充时会得到正确更新,但$ modelValue不会
一旦Chrome自动填充手机字段,我是否可以编程方式更改$modelValue
以反映$scope.PhoneNumber
中应保留的正确值?
答案 0 :(得分:2)
这可以按预期工作,但这绝对令人困惑。 结论:$ setViewValue不能在解析器中调用,因为$ setViewValue本身会触发完整的$ parsers,$ validators管道直到实际的模型值。 那会发生什么? 如果值不同,则调用另一个$ setViewValue。因为管道是同步的,所以它会在第一次运行完成之前再次运行$ parsers!因此,解析器第一次运行时的返回值是实际设置给模型的值。我建议你不要在解析器中调用$ setViewValue,只需设置$ viewValue并调用$ render。
当您没有自动填充时(或简单粘贴也可以),您也可以看到问题。输入1234(单独),您将看到模型为1234,因为这是原始值。仅当输入5时,该值将为123-45,因为此时视图已经使用短划线更新。
它只是角侧的弱API。没有明确的方法来转换视图,因此人们滥用解析器。从技术上讲,我建议覆盖$ setViewValue fn,因此传递给解析器的值已经正确格式化。
更新示例:http://plnkr.co/edit/mAfkQ2DjbCdtFW3oAhBc?p=preview
if (prevValue != nextValue) {
ctrl.$viewValue = nextValue;
ctrl.$render();
}
if (cursorIndex == 4 || cursorIndex == 8) {
cursorIndex = cursorIndex + 1;
}
var valid = phoneNumberRegex.test(nextValue);
ctrl.$setValidity('invalidFormat', valid);
domElement.setSelectionRange(cursorIndex, cursorIndex);
return nextValue;