如何在角度中添加/删除输入来管理焦点/光标位置?

时间:2015-01-11 04:49:12

标签: javascript angularjs angularjs-directive

我正在制作一个指令,当它们全部填满时应自动创建另一个输入字段。所以在这个例子中,当我停止输入第3个输入时,会出现第4个,但我的光标应该保留在第3个输入中。

问题在于,在我的代码中,最终输入始终存在,所以当我更新数据并将其值移动到倒数第二个输入时,它会保留焦点,并且我无法将第二个输入关注到最后一个输入当用户在去抖动间隔结束后快速开始打字时,足够快以防止闪烁和丢失角色。

如何才能正常运作?

此问题的XY版本是:在ng-repeat向DOM添加新元素后,如何同步执行操作?

ui example

plunk

scope: {
    // String[]
    items: '=',

    // 'input' or 'textarea'
    type: '@',

    // 'text', 'email', 'url', etc.
    inputType: '@'
},

link: function (scope, element) {
    scope._inputType = scope.inputType || 'text';
    scope.temp = {
        item: ""
    };

    var $el = jQuery(element.get(0));

    // add another input when the user stops typing in the last input
    scope.$watch('temp.item', (debounce)(function(){
        if (scope.temp.item) {
            scope.items.push(scope.temp.item);
            scope.temp.item = "";
            var elLast = $el.find('.item-last');
            elLast.blur();

            var currentInput = elLast.prev('.item-input').find('input').get(0);
            var tries = 100;

            // use nextTick to reduce time-to-update
            // this often fires a few times before angular actually updates anything
            nextTick(function again(){
                var target = elLast.prev('.item-input').find('input');

                // has angular updated?  if not: retry
                if (target.get(0) === currentInput && (--tries) > 0) {
                    return nextTick(again);
                }

                // focus and move cursor to end
                target.focus();
                var len = target.val().length;
                target.get(0).setSelectionRange(len, len);
            }, 0, false);
        }
    }, 500));

    // remove empty items after a few seconds
    scope.$watch('items', debounce(function(){
        var items = scope.items;
        var inputs = $el.find('.item-input > *');
        var emptyBeforeFocused = 0;
        var focusedIndex = null;

        for (var i=0; i<inputs.length-1; i++) {
            if (inputs.eq(i).is(':focus')) {
                focusedIndex = i;
                break;
            }
            if(!items[i]) {
                emptyBeforeFocused++;
            }
        }

        var originalLength = scope.items.length;
        scope.items = scope.items.filter(x => x.length);

        // we need to fix focus if a previous element was removed
        if (scope.items.length !== originalLength && emptyBeforeFocused > 0 && focusedIndex) {
            scope.$evalAsync(function(){
                // grab the index of the focused input, minus the preceding removed inputs
                // i.e. the input's new index
                var target = $el.find('.item-input > *').eq(focusedIndex - emptyBeforeFocused);

                // focus and move cursor to end
                target.focus();
                var val = scope.type === 'textarea' ? target.text() : target.val();
                var len = val.length;
                target.get(0).setSelectionRange(len, len);
            }, 0, false);
        }
    }, 5000));
}
<div>
    <div ng-if="type === 'input'">
        <div class="item-input item-type-input" ng-repeat="item in items track by $index">
            <input type="text" ng-model="items[$index]" class="form-control" />
        </div>

        <div class="item-input item-type-input item-last">
            <input type="text" ng-model="temp.item" class="form-control" />
        </div>
    </div>
    <div ng-if="type === 'textarea'">
        <div class="item-input item-type-textarea" ng-repeat="item in items track by $index">
            <textarea ng-model="items[$index]" class="form-control" ></textarea>
        </div>
        <div class="item-input item-type-textarea item-last">
            <textarea ng-model="temp.item" class="form-control" ></textarea>
        </div>
    </div>
</div>

1 个答案:

答案 0 :(得分:1)

plnkr

.directive('resetFocus', [function(){
  return function(scope, element, attr, ctrl) {
    if(scope.$last){
      element.find('input').focus();
    }
  };
}])

<div class="item-input item-type-input" ng-repeat="item in items track by $index" reset-focus>
    <input type="text" ng-model="items[$index]" class="form-control" />
</div>

由于你也在删除,你可能需要$ watch('$ last')

.directive('resetFocus', [function(){
  return function(scope, element, attr, ctrl) {
    if(scope.$last){
      element.find('input').focus();
    }
    scope.$watch('$last', function(){
        if(scope.$last){
            element.find('input').focus();
        }
    });
  };
}])

该指令将焦点设置在ng-repeat创建时的最后一个输入元素上。它利用了ng-repeat将在范围中添加$ last的事实。