Ответ 1
Самый простой способ справиться с этим - это, вероятно, написать директиву, которая завершает элемент <input>
и добавляет поведение задержки. Вот директива, которую я написал для этой же цели:
angular.module('MyModule')
.directive('easedInput', function($timeout) {
return {
restrict: 'E',
template: '<div><input class="{{externalClass}} my-eased-input" type="text" ng-model="currentInputValue" ng-change="update()" placeholder="{{placeholder}}"/></div>',
scope: {
value: '=',
timeout: '@',
placeholder: '@',
externalClass: '@class'
},
transclude: true,
link: function ($scope) {
$scope.timeout = parseInt($scope.timeout);
$scope.update = function () {
if ($scope.pendingPromise) { $timeout.cancel($scope.pendingPromise); }
$scope.pendingPromise = $timeout(function () {
$scope.value = $scope.currentInputValue;
}, $scope.timeout);
};
}
}
});
Эта директива будет вызываться в вашем HTML так:
<eased-input value="myValue" timeout="500" placeholder="Please enter text..." />
Раскрытие директивы:
Служба тайм-аута
В этой директиве используется служба angular $timeout
для обработки времени: это инъекционная, макетная, идиоматическая альтернатива вызову setTimeout
. Эта служба вводится в конструктор директивы.
Атрибуты
Директива принимает три атрибута: value
, timeout
и placeholder
.
Атрибут value
здесь привязывается к переменной в области контроллера, которой принадлежит охватывающий "контекст". В этом случае он привязывается к myValue
, то есть к $scope.myValue
от того, какой контроллер отвечает за этот код. Он имеет двустороннюю привязку, обозначенную как запись '='
в свойстве scope
директивы. Это означает, что когда эта директива обновляет value
, изменение распространяется до контроллера, которому принадлежит директива; следовательно, $scope.myValue
изменится, когда value
будет изменено внутри директивы.
Атрибуты timeout
и placeholder
имеют односторонние привязки: директива считывает свои значения из атрибутов, но не изменяет их. Они представляют собой фактические значения конфигурации.
Шаблон HTML
Свойство template
в директиве показывает HTML, который будет сгенерирован на своем месте после того, как angular скомпилирует и свяжет его. Это в основном просто элемент input
с некоторыми специальными и не очень особенными атрибутами. Значение в поле ввода привязано к переменной currentInputValue
в директиве $scope
через ng-model
. Событие change
в поле ввода привязано к функции update
в директиве $scope
с помощью директивы ng-change
.
Функция связи
Гиты процесса лежат в функции link
в директиве: мы определяем метод update
. Как указано выше, этот метод связан с событием change
поля ввода в шаблоне HTML-схемы. Таким образом, каждый раз, когда пользователь меняет ввод в поле, вызывается update
.
Этот метод использует службу $timeout
. Он сообщает службе $timeout
ждать timeout
миллисекунд, а затем применить обратный вызов, который устанавливает $scope.value = $scope.currentInputValue
. Это похоже на вызов setTimeout(function () {$scope.value = $scope.currentInputValue}, timeout)
.
Вызов $timeout
возвращает обещание. Мы можем отменить обещание p
, созданное $timeout
, которое ожидает выполнения, вызывая $timeout.cancel(p)
. Это то, что update
делает в своей первой строке: если у нас есть обещание от предыдущего события изменения, мы отменяем его перед созданием нового. Это означает, что если мы имеем, например, тайм-аут в 500 мс, а обновление вызывается дважды, при этом разнесение на 400 мс, у нас будет только одно обещание, ожидающее пожара.
Общий результат
Обещание при разрешении устанавливает $scope.value = currentInputValue
; то есть он устанавливает свойство "внешне видимое" value
, чтобы иметь значение содержимого поля ввода. value
будет только изменяться - и внешние контроллеры будут видеть только изменение value
- после периода покоя timeout
миллисекунд, который, я считаю, является поведением, которое вы наблюдали после.