J'ai un champ d'entrée de recherche avec une fonction d'interrogation liée au changement de ng.
<input ng-model="search" ng-change="updateSearch()">
Cependant, cela se déclenche trop rapidement sur tous les personnages. Alors je finis par faire quelque chose comme ça beaucoup:
$scope.updateSearch = function(){
$timeout.cancel(searchDelay);
searchDelay = $timeout(function(){
$scope.requery($scope.search);
},300);
}
De sorte que la demande ne soit faite que 300 ms après que l'utilisateur a cessé de taper. Existe-t-il une solution pour intégrer cela dans une directive?
À partir de la version 1.3 angulaire, c’est plus facile à réaliser avec ngModelOptions :
<input ng-model="search" ng-change="updateSearch()" ng-model-options="{debounce:3000}">
Syntax: {debounce: Miliseconds}
Pour résoudre ce problème, j'ai créé une directive appelée ngDelay.
ngDelay augmente le comportement de ngChange pour prendre en charge le comportement retardé souhaité, qui fournit des mises à jour chaque fois que l'utilisateur est inactif, plutôt que sur chaque frappe. L'astuce consistait à utiliser une portée enfant et à remplacer la valeur de ngChange par un appel de fonction incluant la logique de délai d'attente et exécutant l'expression d'origine sur la portée parent. La deuxième astuce consistait à déplacer les liaisons ngModel vers la portée parente, le cas échéant. Ces modifications sont toutes effectuées dans la phase de compilation de la directive ngDelay.
Voici un violon qui contient un exemple utilisant ngDelay: http://jsfiddle.net/ZfrTX/7/ (Écrit et édité par moi, avec l'aide de mainguy et Ryan Q)
Vous pouvez trouver ce code sur GitHub grâce à brentvatne . Merci Brent!
Pour une référence rapide, voici le code JavaScript de la directive 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);
}
};
}
}
}
};
}]);
Et s'il y a des trucs TypeScript, voici le TypeScript utilisant les définitions angulaires de 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>;
}
Cela fonctionne parfaitement pour moi: 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:'&'
}
}
});
Utilisation de la directive
Dans votre contrôleur:
app.controller('ctrl', function ($scope,$timeout) {
$scope.requery = function (search) {
console.log(search);
}
});
A votre avis:
<div ng-app="app">
<div ng-controller="ctrl">
<delay-search delayed-method="requery(search)"></delay-search>
</div>
</div>
Je sais que je suis en retard pour le jeu, mais j'espère que cela aidera tous ceux qui utilisent encore la version 1.2. Pre-ng-model-options J'ai trouvé que cela fonctionnait pour moi, car ngchange ne se déclenche pas lorsque la valeur est invalide.
ceci est une légère variation de la réponse de @doug car elle utilise ngKeypress, qui se moque de l'état du modèle.
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;
};