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.