如何使过滤后的输入能够在其中进行编辑,而不是在最后?

How do I make a filtered input able to have edits occur inside it, not at the end?

在我正在进行的一个项目中,我的团队最近在 JavaScript/Angular 中开发了一个 'masked' 输入,作为依赖于过滤器的指令,以呈现 phone 号码作为 U.S 给用户。格式化 phone 数字。因为我们的应用程序只会在美国使用,所以我们不必担心让它灵活地适应其他国家的 phone 格式。

对于那些不熟悉的人,这是 U.S 的常用格式。电话phone 号码:

(AAA) EEE-TTTT xXXXXX

其中:

我最初打算解决的问题是,尝试编辑区号会导致光标跳到 phone 号码的末尾。更深层次的问题是,由于我们当前对该指令的实现,尝试在 phone 数字的中间插入任何信息会导致光标跳到末尾!

下面是它的实现和使用方式:

标记:

<input type="text"
       id="some-phone-number"
       name="somePhoneNumber"
       class="form-control"
       data-ng-model="vm.somePhoneNumber" 
       phone-input />

指令:

angular.module('app').directive('phoneInput', [
    '$filter', '$browser', phoneInputDir
]);

function phoneInputDir($filter, $browser) {
    return {
        require: 'ngModel',
        link: function($scope, $element, $attrs, ngModelCtrl) {
            var listener = function() {
                var value = $element.val().replace(/[^0-9]/g, '');
                $element.val($filter('tel')(value, false));
            };

            ngModelCtrl.$formatters.unshift(function (modelValue) {
                return $filter('tel')(modelValue, false);
            });

            // This runs when we update the text field
            ngModelCtrl.$parsers.push(function(viewValue) {
                return viewValue.replace(/[^0-9]/g, '').slice(0, 15);
            });

            // This runs when the model gets updated on the scope directly and keeps our view in sync
            ngModelCtrl.$render = function() {
                $element.val($filter('tel')(ngModelCtrl.$viewValue, false));
            };

            $element.bind('change', listener);
            $element.bind('keydown', function(event) {
                var key = event.keyCode;
                // If the keys include the CTRL, SHIFT, ALT, or META keys, or the arrow keys, do nothing.
                // This lets us support copy and paste too
                if (key == 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) {
                    return;
                }
                $browser.defer(listener); // Have to do this or changes don't get picked up properly
            });

            $element.bind('paste cut', function() {
                $browser.defer(listener);
            });
        }
    };
}    

过滤器:

angular.module('app').filter('tel', function() {
    return function(input) {
        console.log(input);
        if (!input) {
            return '';
        }

        var value = input.toString().trim().replace(/^\+/, '');

        if (value.match(/[^0-9]/)) {
            return input;
        }

        // Phone number format: 
        // (AAA) EEE-TTTT xXXXXX...
        var areaCode = value.slice(0, 3),
            exchangeCode = value.slice(3, 6),
            terminalCode = value.slice(6, 10),
            extension = value.slice(10);

        var result = '';
        if (areaCode)
            result += '(' + areaCode + ') ';
        if (exchangeCode)
            result += exchangeCode;
        if (terminalCode)
            result += '-' + terminalCode;
        if (extension)
            result += ' x' + extension;

        return result;
    };
});

问题: 我可以用什么方式更改这里的 directive/filter 以便能够进行内联编辑,而不是任何导致光标移动到末尾的按键输入的?

由于您是动态更新输入中的文本,因此您必须在更新前存储光标位置,然后在更新后设置它。您还需要将一个添加到光标位置以说明您要插入的字符。为此,您可以在指令中修改侦听器函数,如下所示:

var listener = function() {
    var cursorPos = $element[0].selectionStart + 1;
    var value = $element.val().replace(/[^0-9]/g, '');
    $element.val($filter('tel')(value, false));
    $element[0].setSelectionRange(cursorPos, cursorPos);
};

由于 $element 是一个选择器而不是实际的 HTML 元素本身,您需要引用第一个子元素( HTML 元素) 这就是必须使用 $element[0] 的原因。