Ответ 1
Как и в случае с angular 1.3, это проще выполнить, используя ngModelOptions:
<input ng-model="search" ng-change="updateSearch()" ng-model-options="{debounce:3000}">
Syntax: {debounce: Miliseconds}
У меня есть поле ввода поиска с функцией requery, связанной с ng-change.
<input ng-model="search" ng-change="updateSearch()">
Однако это срабатывает слишком быстро для каждого персонажа. Поэтому я в конечном итоге делаю что-то вроде этого alot:
$scope.updateSearch = function(){
$timeout.cancel(searchDelay);
searchDelay = $timeout(function(){
$scope.requery($scope.search);
},300);
}
Таким образом, запрос будет выполнен только через 300 мс после того, как пользователь перестанет печатать. Есть ли какое-либо решение, чтобы обернуть это в директиве?
Как и в случае с angular 1.3, это проще выполнить, используя ngModelOptions:
<input ng-model="search" ng-change="updateSearch()" ng-model-options="{debounce:3000}">
Syntax: {debounce: Miliseconds}
Чтобы решить эту проблему, я создал директиву ngDelay.
ngDelay увеличивает поведение ngChange для поддержки желаемого отложенного поведения, которое предоставляет обновления всякий раз, когда пользователь неактивен, а не на каждом нажатии клавиши. Фокус в том, чтобы использовать дочернюю область и заменить значение ngChange на вызов функции, который включает логику тайм-аута и выполняет исходное выражение в родительской области. Второй трюк состоял в том, чтобы перенести любые привязки ngModel в родительскую область, если они есть. Все эти изменения выполняются на этапе компиляции директивы ngDelay.
Здесь сценарий, содержащий пример с использованием ngDelay: http://jsfiddle.net/ZfrTX/7/ (Написано и отредактировано мной, с помощью mainguy и Ryan Q)
Этот код можно найти на GitHub благодаря brentvatne. Спасибо Брент!
Для быстрой справки, здесь JavaScript для директивы ngDelay:
app.directive('ngDelay', ['$timeout', function ($timeout) {
return {
restrict: 'A',
scope: true,
compile: function (element, attributes) {
var expression = attributes['ngChange'];
if (!expression)
return;
var ngModel = attributes['ngModel'];
if (ngModel) attributes['ngModel'] = '$parent.' + ngModel;
attributes['ngChange'] = '$$delay.execute()';
return {
post: function (scope, element, attributes) {
scope.$$delay = {
expression: expression,
delay: scope.$eval(attributes['ngDelay']),
execute: function () {
var state = scope.$$delay;
state.then = Date.now();
$timeout(function () {
if (Date.now() - state.then >= state.delay)
scope.$parent.$eval(expression);
}, state.delay);
}
};
}
}
}
};
}]);
И если есть TypeScript wonks, здесь TypeScript с использованием определений angular от DefinitelyTyped:
components.directive('ngDelay', ['$timeout', ($timeout: ng.ITimeoutService) => {
var directive: ng.IDirective = {
restrict: 'A',
scope: true,
compile: (element: ng.IAugmentedJQuery, attributes: ng.IAttributes) => {
var expression = attributes['ngChange'];
if (!expression)
return;
var ngModel = attributes['ngModel'];
if (ngModel) attributes['ngModel'] = '$parent.' + ngModel;
attributes['ngChange'] = '$$delay.execute()';
return {
post: (scope: IDelayScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes) => {
scope.$$delay = {
expression: <string>expression,
delay: <number>scope.$eval(attributes['ngDelay']),
execute: function () {
var state = scope.$$delay;
state.then = Date.now();
$timeout(function () {
if (Date.now() - state.then >= state.delay)
scope.$parent.$eval(expression);
}, state.delay);
}
};
}
}
}
};
return directive;
}]);
interface IDelayScope extends ng.IScope {
$$delay: IDelayState;
}
interface IDelayState {
delay: number;
expression: string;
execute(): void;
then?: number;
action?: ng.IPromise<any>;
}
Это отлично работает для меня: JSFiddle
var app = angular.module('app', []);
app.directive('delaySearch', function ($timeout) {
return {
restrict: 'EA',
template: ' <input ng-model="search" ng-change="modelChanged()">',
link: function ($scope, element, attrs) {
$scope.modelChanged = function () {
$timeout(function () {
if ($scope.lastSearch != $scope.search) {
if ($scope.delayedMethod) {
$scope.lastSearch = $scope.search;
$scope.delayedMethod({ search: $scope.search });
}
}
}, 300);
}
},
scope: {
delayedMethod:'&'
}
}
});
Использование директивы
В вашем контроллере:
app.controller('ctrl', function ($scope,$timeout) {
$scope.requery = function (search) {
console.log(search);
}
});
На ваш взгляд:
<div ng-app="app">
<div ng-controller="ctrl">
<delay-search delayed-method="requery(search)"></delay-search>
</div>
</div>
Я знаю, что опаздываю в игру, но, надеюсь, это поможет любому, кто все еще использует 1.2. Pre-n-model-options Я нашел, что это сработало для меня, поскольку ngchange не будет срабатывать, когда значение недействительно.
это небольшое отклонение от ответа @doug, поскольку оно использует ngKeypress, которому не важно, в каком состоянии находится модель.
function delayChangeDirective($timeout) {
var directive = {
restrict: 'A',
priority: 10,
controller: delayChangeController,
controllerAs: "$ctrl",
scope: true,
compile: function compileHandler(element, attributes) {
var expression = attributes['ngKeypress'];
if (!expression)
return;
var ngModel = attributes['ngModel'];
if (ngModel) {
attributes['ngModel'] = '$parent.' + ngModel;
}
attributes['ngKeypress'] = '$$delay.execute()';
return {
post: postHandler,
};
function postHandler(scope, element, attributes) {
scope.$$delay = {
expression: expression,
delay: scope.$eval(attributes['ngKeypressDelay']),
execute: function () {
var state = scope.$$delay;
state.then = Date.now();
if (scope.promise) {
$timeout.cancel(scope.promise);
}
scope.promise = $timeout(function() {
delayedActionHandler(scope, state, expression);
scope.promise = null;
}, state.delay);
}
};
}
}
};
function delayedActionHandler(scope, state, expression) {
var now = Date.now();
if (now - state.then >= state.delay) {
scope.$parent.$eval(expression);
}
};
return directive;
};