AngularJS 빠른 검색을 지연시키는 방법은 무엇입니까?


147

해결할 수없는 성능 문제가 있습니다. 인스턴트 검색이 있지만 각에서 검색을 시작하기 때문에 다소 게으 릅니다 keyup().

JS :

var App = angular.module('App', []);

App.controller('DisplayController', function($scope, $http) {
$http.get('data.json').then(function(result){
    $scope.entries = result.data;
});
});

HTML :

<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:searchText">
<span>{{entry.content}}</span>
</div>

JSON 데이터는 300KB 정도로 크지 않습니다. 필자가 달성해야 할 것은 각 키 입력에 대한 작업을 수행하는 대신 사용자가 입력을 마칠 때까지 검색을 ~ 1 초 지연시키는 것입니다. AngularJS는 내부적 으로이 작업을 수행하며 여기에서 문서 및 기타 주제를 읽은 후에는 구체적인 답변을 찾을 수 없었습니다.

인스턴트 검색을 지연시킬 수있는 방법에 대한 조언을 부탁드립니다.


1
init 앱에서 모든 json을 얻는 중이며 검색 필터가 입력 할 때 두 번째로 데이터를 얻지 못합니다. 이미 기존 모델을 필터링합니다. 나 맞아?
Maksym

아래 답변이 해결 되었습니까? 그렇다면 답변을 수락하십시오. 그렇지 않은 경우 알려 주시면 더 자세히 설명하겠습니다.
Jason Aden

Jason, 응답 주셔서 감사합니다. 나는 당신의 코드를 가지고 놀려고했지만 운이 없습니다. 검색이 완전히 작동하지 않습니다.
braincomb

신경 쓰지 마라, 내가 간과했던 것은 나의 잘못이었다. 귀하의 솔루션은 실제로 작동합니다. 감사합니다 :)
braincomb

이 답변을 여기에서 살펴보십시오. ng-change를 지연시킬 수있는 지시문을 제공합니다 : stackoverflow.com/questions/21121460/…
Doug

답변:


121

(Angular 1.3 솔루션에 대해서는 아래 답변을 참조하십시오.)

여기서 문제는 모델이 변경 될 때마다 검색이 실행되며 입력에 대한 모든 키업 동작입니다.

이 작업을 수행하는 더 확실한 방법이 있지만 가장 쉬운 방법은 필터가 작동하는 컨트롤러 내부에 $ scope 속성이 정의되도록 바인딩을 전환하는 것입니다. 그렇게하면 $ scope 변수가 얼마나 자주 업데이트되는지 제어 할 수 있습니다. 이 같은:

JS :

var App = angular.module('App', []);

App.controller('DisplayController', function($scope, $http, $timeout) {
    $http.get('data.json').then(function(result){
        $scope.entries = result.data;
    });

    // This is what you will bind the filter to
    $scope.filterText = '';

    // Instantiate these variables outside the watch
    var tempFilterText = '',
        filterTextTimeout;
    $scope.$watch('searchText', function (val) {
        if (filterTextTimeout) $timeout.cancel(filterTextTimeout);

        tempFilterText = val;
        filterTextTimeout = $timeout(function() {
            $scope.filterText = tempFilterText;
        }, 250); // delay 250 ms
    })
});

HTML :

<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:filterText">
    <span>{{entry.content}}</span>
</div>

angular-ui 부트 스트랩의 모달 에서는 $ scope. $ watch ng-model가 작동하지 않습니다.
Hendy Irawan

1
tempFilterText 변수 없이도 작동한다고 생각합니다. $ scope. $ watch ( 'searchText', function (val) {if (filterTextTimeout) $ timeout.cancel (filterTextTimeout); filterTextTimeout = $ timeout (function () {$ scope. filterText = val;}, 250); // 지연 250 ms})
Jos Theeuwen

@JosTheeuwen 그것은 단순히 나쁜 습관으로 간주되고 엄격 모드 에서는 허용되지 않는 전역 변수입니다 .
mb21 2016 년

301

최신 정보

이제 그 어느 때보 다 쉬워졌습니다 (Angular 1.3). 모델에 디 바운스 옵션을 추가하기 만하면됩니다.

<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">

업데이트 된 플 런커 :
http://plnkr.co/edit/4V13gK

ngModelOptions에 대한 설명서 :
https://docs.angularjs.org/api/ng/directive/ngModelOptions

오래된 방법 :

각도 자체를 넘어 의존성이없는 또 다른 방법이 있습니다.

시간 초과를 설정하고 현재 문자열을 이전 버전과 비교해야합니다. 둘 다 동일하면 검색을 수행합니다.

$scope.$watch('searchStr', function (tmpStr)
{
  if (!tmpStr || tmpStr.length == 0)
    return 0;
   $timeout(function() {

    // if searchStr is still the same..
    // go ahead and retrieve the data
    if (tmpStr === $scope.searchStr)
    {
      $http.get('//echo.jsontest.com/res/'+ tmpStr).success(function(data) {
        // update the textarea
        $scope.responseData = data.res; 
      });
    }
  }, 1000);
});

그리고 이것은 당신의 견해에 들어갑니다 :

<input type="text" data-ng-model="searchStr">

<textarea> {{responseData}} </textarea>

필수 플 런커 : http://plnkr.co/dAPmwf


2
나를 위해 그것은 받아 들여지는 것보다 훨씬 이해하기 쉬운 대답입니다 :) 감사합니다!
OZ_

3
여러 모델 변경 사항이 누적되어 요청이 중복되는 문제가 없습니까? @JasonAden의 답변에서 그는 이전에 대기열에 있던 이벤트를 취소하여 처리합니다.
Blaskovicz

이론적으로 모델에 변경이 발생하지만 데이터가 동일하게 유지되면 여러 요청이 발생합니다. 실제로 나는 그것이 일어난 것을 본 적이 없다. 걱정되는 경우 해당 사례를 확인하는 플래그를 추가 할 수 있습니다.
Josue Alexander Ibarra

이것은 각도 1.3에 대한 탁월한 선택입니다
Marcus W

여기 경고 : 제출 또는 트리거하는 키 누르기 이벤트가있는 경우 값 바인딩이 디 바운스되므로 최신 모델 값없이 수행됩니다. 예를 들어 'foo'를 입력하고 즉시 키를 누르면 값이 여전히 빈 문자열이됩니다.
jbodily

34

Angular 1.3에서 나는 이것을 할 것이다 :

HTML :

<input ng-model="msg" ng-model-options="{debounce: 1000}">

제어 장치:

$scope.$watch('variableName', function(nVal, oVal) {
    if (nVal !== oVal) {
        myDebouncedFunction();
    }
});

기본적으로 범위 변수가 변경 myDebouncedFunction()되면 angular에게 run을 지시합니다 msg. 이 속성을 ng-model-options="{debounce: 1000}"사용 msg하면 1 초에 한 번만 업데이트 할 수 있습니다.


10
 <input type="text"
    ng-model ="criteria.searchtext""  
    ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
    class="form-control" 
    placeholder="Search" >

이제 ng-model-options 디 바운스를 시간과 함께 설정할 수 있으며 블러, 모델을 즉시 변경해야 할 경우 저장하지 않으면 지연이 완료되지 않으면 이전 값을 갖습니다.


9

HTML 마크 업에서 키업 / 키 다운을 사용하는 사람들을 위해. 시계를 사용하지 않습니다.

JS

app.controller('SearchCtrl', function ($scope, $http, $timeout) {
  var promise = '';
  $scope.search = function() {
    if(promise){
      $timeout.cancel(promise);
    }
    promise = $timeout(function() {
    //ajax call goes here..
    },2000);
  };
});

HTML

<input type="search" autocomplete="off" ng-model="keywords" ng-keyup="search()" placeholder="Search...">

6

angularjs에 대한 디 바운스 / 스로틀 모델 업데이트 : http://jsfiddle.net/lgersman/vPsGb/3/

귀하의 경우 jsfiddle 코드에서 지시문을 다음과 같이 사용하는 것 외에는 할 일이 없습니다.

<input 
    id="searchText" 
    type="search" 
    placeholder="live search..." 
    ng-model="searchText" 
    ng-ampere-debounce
/>

기본적으로 http://benalman.com/projects/jquery-throttle-debounce-plugin/ 을 사용 하는 "ng-ampere-debounce"라는 단일 각도 지시문으로 구성된 작은 코드로 모든 dom 요소에 첨부 할 수 있습니다. 지시문은 첨부 된 이벤트 핸들러를 재정렬하여 이벤트를 조절할시기를 제어 할 수 있도록합니다.

스로틀 링 / 토론에 사용할 수 있습니다 * 모델 각도 업데이트 * 각도 이벤트 핸들러 ng- [event] * jquery 이벤트 핸들러

보세요 : http://jsfiddle.net/lgersman/vPsGb/3/

이 지침은 Orangevolt Ampere 프레임 워크 ( https://github.com/lgersman/jquery.orangevolt-ampere )의 일부입니다 .


6

여기에 리디렉션 된 사용자의 경우 :

소개로 Angular 1.3당신이 사용할 수있는 모델-옵션 NG 속성 :

<input 
       id="searchText" 
       type="search" 
       placeholder="live search..." 
       ng-model="searchText"
       ng-model-options="{ debounce: 250 }"
/>

5

이 문제를 해결하는 가장 좋은 방법은 Ben Alman의 플러그인 jQuery throttle / debounce를 사용하는 것 입니다. 제 생각에는 양식의 모든 단일 필드 이벤트를 지연시킬 필요가 없습니다.

$ scope. $ watch 처리 기능을 $ .debounce로 다음과 같이 감싸십시오.

$scope.$watch("searchText", $.debounce(1000, function() {
    console.log($scope.searchText);
}), true);

당신은 $ 범위에서이 포장해야합니다 $ 적용됩니다.
Aakil 페르난데스

3

또 다른 솔루션은 모델 업데이트에 지연 기능을 추가하는 것입니다. 간단한 지시문은 트릭을 수행하는 것 같습니다.

app.directive('delayedModel', function() {
    return {
        scope: {
            model: '=delayedModel'
        },
        link: function(scope, element, attrs) {

            element.val(scope.model);

            scope.$watch('model', function(newVal, oldVal) {
                if (newVal !== oldVal) {
                    element.val(scope.model);        
                }
            });

            var timeout;
            element.on('keyup paste search', function() {
                clearTimeout(timeout);
                timeout = setTimeout(function() {
                    scope.model = element[0].value;
                    element.val(scope.model);
                    scope.$apply();
                }, attrs.delay || 500);
            });
        }
    };
});

용법:

<input delayed-model="searchText" data-delay="500" id="searchText" type="search" placeholder="live search..." />

그래서 당신은 단지 delayed-model대신 사용 ng-model하고 정의합니다 data-delay.

데모 : http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview


야! 어떻게 model: '=delayedModel'작동 하는지 설명 할 수 있습니까? 아니면 내가 찾을 수있는 링크를 알려주시겠습니까?
Akash Agrawal

@AkashAgrawal 양방향 데이터 바인딩입니다. 여기에서 읽으십시오 docs.angularjs.org/api/ng.$compile
dfsq

1
@dfsq 나는 ng-change를 사용하고 있었고 텍스트가 변경 될 때마다 트리거하는 데 사용되었습니다. 그러나 지시어가 정의되어 있으면 사용할 수 없습니다. element.on('change')흐림에서만 트리거합니다. (1) 해결 방법이 있습니까? (2) 텍스트 변경시 컨트롤러 기능을 호출하는 방법은 무엇입니까?
Vyas Rao

0

기본적으로 그것이 수행하는 지시어를 사용하여 지시문 으로이 문제를 해결했습니다. 지시문에서보고있는 특수 속성에 실제 ng 모델을 바인딩 한 다음 디 바운스 서비스를 사용하여 지시문 속성을 업데이트하므로 사용자는 변수를 감시합니다. 그는 ng-model 대신 debounce-model에 바인딩합니다.

.directive('debounceDelay', function ($compile, $debounce) {
return {
  replace: false,
  scope: {
    debounceModel: '='
  },
  link: function (scope, element, attr) {
    var delay= attr.debounceDelay;
    var applyFunc = function () {
      scope.debounceModel = scope.model;
    }
    scope.model = scope.debounceModel;
    scope.$watch('model', function(){
      $debounce(applyFunc, delay);
    });
    attr.$set('ngModel', 'model');
    element.removeAttr('debounce-delay'); // so the next $compile won't run it again!

   $compile(element)(scope);
  }
};
});

용법:

<input type="text" debounce-delay="1000" debounce-model="search"></input>

그리고 컨트롤러에서 :

    $scope.search = "";
    $scope.$watch('search', function (newVal, oldVal) {
      if(newVal === oldVal){
        return;
      }else{ //do something meaningful }

jsfiddle의 데모 : http://jsfiddle.net/6K7Kd/37/

$ debounce 서비스는 여기에서 찾을 수 있습니다 : http://jsfiddle.net/Warspawn/6K7Kd/

finallyBind 지시어에서 영감을 받음 http://jsfiddle.net/fctZH/12/


0

Angular 1.3에는 ng-model-options 디 바운스가 있지만, 그때까지 Josue Ibarra와 같은 타이머를 사용해야합니다. 그러나 그의 코드에서 그는 모든 키를 누를 때마다 타이머를 시작합니다. 또한 Angular가 $ timeout을 사용하거나 setTimeout의 끝에서 $ apply를 사용해야하는 경우 setTimeout을 사용하고 있습니다.


0

모두가 왜 시계를 사용하고 싶어합니까? 함수를 사용할 수도 있습니다.

var tempArticleSearchTerm;
$scope.lookupArticle = function (val) {
    tempArticleSearchTerm = val;

    $timeout(function () {
        if (val == tempArticleSearchTerm) {
            //function you want to execute after 250ms, if the value as changed

        }
    }, 250);
}; 

0

가장 쉬운 방법은 json을 미리로드하거나 한 번로드 $dirty하면 필터 검색이 나머지를 처리하는 것입니다. 이렇게하면 추가 HTTP 호출이 저장되고 미리로드 된 데이터로 훨씬 빨라집니다. 기억은 아프지 만 그만한 가치가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.