【问题标题】:Chrome autofill AngularJS field bugChrome自动填充AngularJS字段错误
【发布时间】:2016-03-12 12:35:39
【问题描述】:

我正在尝试解决我在使用自定义 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 autofill 我的电话号码字段,值为“1234567899”时,我的$scope Angular 电话字段的值为 1234567899(它应该是:“123-546- 7899")。

在我的指令中放置一个断点将使我看到当 Chrome 自动填充我的字段时该指令确实在我的浏览器中运行,但 $scope.PhoneNumber 字段的值是 1234567899,而不是 123-456-7899。 简而言之,自动填充时 $viewModel 会正确更新,但 $modelValue 不会

Chrome 自动填充电话字段后,我能否以编程方式更改 $modelValue 以反映应保留在 $scope.PhoneNumber 中的正确值?

PLUNKR

【问题讨论】:

  • 您能否在 plnkr.co 中添加一个可运行的示例?
  • 是的,我会为这个问题找一个人
  • 添加了一个 plunkr,在浏览链接之前,您必须有一个不包含破折号的自动填充电话号码

标签: javascript angularjs google-chrome


【解决方案1】:

这按预期工作,但绝对令人困惑。 底线:$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;

【讨论】:

  • 感谢您指出这一点,我不明白发生了什么。但是,是对$render() 的调用将$viewValue 同步到$modelValue?我只是想确保我理解这一点
  • Angular 核心仅在模型更改时才调用 $render。然后顺序是这样的:格式化程序 -> 分配 viewValue -> 验证器 -> 渲染()。当视图因输入事件等发生变化时,顺序为 setViewValue - 解析器 - 验证器 - 模型更新。
  • 感谢您澄清这一点
猜你喜欢
  • 2016-03-30
  • 2019-10-12
  • 2017-11-21
  • 2019-10-16
  • 2018-05-21
  • 1970-01-01
  • 1970-01-01
  • 2013-06-08
  • 1970-01-01
相关资源
最近更新 更多