directiva angular que encapsula un retraso para ng-change

Tengo un campo de entrada de búsqueda con una función de consulta ligada al ng-change.

 

Sin embargo, esto dispara demasiado rápido en cada personaje. Así que termino haciendo algo como esto mucho:

  $scope.updateSearch = function(){ $timeout.cancel(searchDelay); searchDelay = $timeout(function(){ $scope.requery($scope.search); },300); } 

De modo que la solicitud solo se realiza 300 ms después de que el usuario haya dejado de escribir. ¿Hay alguna solución para ajustar esto en una directiva?

A partir de 1.3 angular esto es mucho más fácil de lograr, usando ngModelOptions :

  Syntax: {debounce: Miliseconds} 

Para resolver este problema, creé una directiva llamada ngDelay.

ngDelay aumenta el comportamiento de ngChange para admitir el comportamiento retrasado deseado, que proporciona actualizaciones cada vez que el usuario está inactivo, en lugar de cada pulsación de tecla. El truco consistía en utilizar un ámbito hijo y reemplazar el valor de ngChange en una llamada a función que incluye la lógica de tiempo de espera y ejecuta la expresión original en el ámbito principal. El segundo truco fue mover cualquier enlace ngModel al scope principal, si está presente. Todos estos cambios se realizan en la fase de comstackción de la directiva ngDelay.

Aquí hay un violín que contiene un ejemplo usando ngDelay: http://jsfiddle.net/ZfrTX/7/ (Escrito y editado por mí, con la ayuda de mainguy y Ryan Q)

Puedes encontrar este código en GitHub gracias a brentvatne . Gracias Brent!

Para una referencia rápida, aquí está el JavaScript para la directiva 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); } }; } } } }; }]); 

Y si hay algún tipo de comodines de TypeScript, aquí está el TypeScript que usa las definiciones angulares 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: 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); } }; } } } }; return directive; }]); interface IDelayScope extends ng.IScope { $$delay: IDelayState; } interface IDelayState { delay: number; expression: string; execute(): void; then?: number; action?: ng.IPromise; } 

Esto funciona perfectamente para mí: JSFiddle

  var app = angular.module('app', []); app.directive('delaySearch', function ($timeout) { return { restrict: 'EA', template: ' ', 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:'&' } } }); 

Usando la directiva

En tu controlador:

 app.controller('ctrl', function ($scope,$timeout) { $scope.requery = function (search) { console.log(search); } }); 

En su opinión: