Простое решение этой проблемы находится тут: jsfiddle.net/nZdgm/
Представим, что у вас есть список ($scope.list), который вы публикуете как фильтрованный список ($scope.filteredList) на основе чего-либо содержащего текст из поля $scope.searchText. Ваша форма выглядела бы примерно следующим образом (не обращайте внимание на чекбокс throttle пока что):
<div data-ng-app='App'> <div data-ng-controller="MyCtrl"> <form> <label for="searchText">Search Text:</label> <input data-ng-model="searchText" name="searchText" /> <input type="checkbox" data-ng-model="throttle"> Throttle <label>You typed:</label> <span>{{searchText}}</span> </form> <ul><li data-ng-repeat="item in filteredList">{{item}}</li></ul> </div> </div>
Типичный сценарий — наблюдать за полем поиска и реагировать мгновенно. Метод фильтрации:
var filterAction = function($scope) { if (_.isEmpty($scope.searchText)) { $scope.filteredList = $scope.list; return; } var searchText = $scope.searchText.toLowerCase(); $scope.filteredList = _.filter($scope.list, function(item) { return item.indexOf(searchText) !== -1; }); };
Контроллер устанавливает $watch примерно следующим образом:
$scope.$watch('searchText', function(){filterAction($scope);});
Такой подход будет запускать фильтрацию каждый раз при вводе в поле. Чтобы устаканить ситуацию, используем встроенную в underscore.js функцию debounce. Функция довольна проста: передайте ей функцию для выполнения и время в миллисекундах. Это задержит реальный вызов функции до тех пор, пока с момента последней попытки ее вызова не пройдет указанное время. Другими словами, при задержке в 1 секунду (которую я использую в этом примере для утрирования эффекта) и непрерывном потоке вызовов функции во время быстрого ввода текста в поле, реальная функция не будет вызвана до тех пор, пока я не прекращу печатать и с этого момента не пройдет 1 секунда.
Может возникнуть искушение сделать простой debounce примерно таким образом:
var filterThrottled = _.debounce(filterAction, 1000); $scope.$watch('searchText', function(){filterThrottled($scope);});
Однако, тут есть проблема. Такой подход использует таймер, который срабатывает вне цикла $digest, поэтому это в итоге никак не отразится на UI, ведь Angular не знает о случившихся изменениях. Вместо этого вы должны обернуть вызов в $apply:
var filterDelayed = function($scope) { $scope.$apply(function(){filterAction($scope);}); };
После этого вы можете установить $watch и отреагировать, как только ввод остановится:
var filterThrottled = _.debounce(filterDelayed, 1000); $scope.$watch('searchText', function(){filterThrottled($scope);});
Конечно, полноценный пример тут должен включать в себя и throttling, так чтобы можно было увидеть разницу между «мгновенной» фильтрацией и задержанной. Фидл на случай можно посмотреть здесь: jsfiddle.net/nZdgm/
ссылка на оригинал статьи http://habrahabr.ru/post/210344/
Добавить комментарий