Переменная модели Angular обновляется слишком рано перед обновлением DOM, что приводит к отставанию пользовательского интерфейса?

Я пытаюсь создать директиву тега Angular, которая не слишком отличается от формы ввода тега Stack Overflow. Он использует div с рамкой для создания иллюзии формы, а затем div, содержащий список тегов слева от поля ввода, где вы можете ввести:

<div class="wrapper">
    <div class="tag-wrapper">
        <div class="tags" ng-repeat="tag in selectedTags">
            <div>
                [[ tag.name ]]
                <span class="remove" ng-click="removeTag(tag)"></span>
            </div>
        </div>
    </div>
    <input type="text" class="tag-input"
           ng-model="tagInput"
           ng-style="{ width: inputLength + 'px'}"
           ng-keypress="tagInputKeyPress($event)"
           ng-keyup="updateSuggestionList()"
           ng-focus="toggleSuggestionVisibility()"
           ng-blur="toggleSuggestionVisibility()" />
</div>

Обратите внимание, что я использую [[]] в качестве поставщика интерполяции для Angular, потому что у меня есть другой механизм шаблонов, уже использующий {{}}.

Когда клавиша нажата на входе, она запускает функцию, чтобы проверить, является ли клавиша возвратом или вводом или пробелом для создания/удаления тега:

$scope.tagInputKeyPress = function(event) {
    // Currently using jQuery.event.which to detect keypresses, keyCode is deprecated, use KeyboardEvent.key eventually:
    // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key

    // event.key == ' ' || event.key == 'Enter'
    if (event.which == 32 || event.which == 13) {
        event.preventDefault();

        // Remove any rulebreaking chars
        var tag = $scope.tagInput;
        tag = tag.replace(/["']/g, "");
        // Remove whitespace if present
        tag = tag.trim();

        $scope.createTag(tag);

    // event.key == 'Backspace'
    } else if (event.which == 8 && $scope.tagInput == "") {
        event.preventDefault();

        // grab the last tag to be inserted (if any) and put it back in the input
        if ($scope.selectedTags.length > 0) {
            $scope.tagInput = $scope.selectedTags.pop().name;
        }
    }
    $scope.inputLength = $(element).find('input.tag-input').parent().innerWidth() - $(element).find('.tag-wrapper').outerWidth() - 1;

    return true;
};

У меня проблемы с последней строкой перед оператором return. Что нужно сделать, так это пересчитать ширину элемента .tag-wrapper и отрегулировать ширину элемента input в соответствии с этим свойством ng-style:

ng-style="{ width: inputLength + 'px'}"

Однако в настоящее время длина ввода всегда отстает от пользовательского интерфейса на один шаг, что приводит к переполнению ввода. Это потому что:

  1. Сначала он добавляет новый тег в список selectedTags.
  2. Он вычисляет ширину .tag-wrapper.
  3. Он возвращает true, пропуская исходное ключевое событие.
  4. Цикл дайджеста Angular запускается и добавляет новый тег в DOM. Ширина .tag-wrapper теперь больше, а input выходит за пределы.

Вот пример этого:

введите здесь описание изображения

Прямо сейчас шаг 4 выполняется после шага 2. Мне нужно, чтобы шаг 2 выполнялся после шага 4.

Как я могу этого добиться?


person ReactingToAngularVues    schedule 29.08.2015    source источник


Ответы (1)


Вы изменяете свой метод createTag, чтобы иметь обратный вызов, и устанавливаете inputLength в обратном вызове.

Альтернативный хак: оберните настройку inputLength внутри $timeout, чтобы заставить строку выполняться в следующем вызове дайджеста.

person sahbeewah    schedule 29.08.2015
comment
Кажется, не удается получить обратный вызов, чтобы решить эту проблему. На самом деле это просто приводит к еще большему отставанию ввода. Можете ли вы привести пример? Кроме того, идея тайм-аута, хотя и умная, вызывает мерцание, что не идеально. - person ReactingToAngularVues; 30.08.2015
comment
Закончился с тайм-аутом 0 миллисекунд и установкой overflow:hidden; для содержащего элемента. - person ReactingToAngularVues; 06.09.2015