AngularJS anlık aramasında gecikme nasıl yapılır?


147

Çözemediğim bir performans sorunum var. Anında arama yapıyorum ama her birinde arama yapmaya başladığı için biraz laggy 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 verileri bile o kadar büyük değil, sadece 300KB, bence başarmam gereken şey her tuş vuruşunda eylem yerine kullanıcının yazmayı bitirmesini beklemek için aramada ~ 1 sn bir gecikme koymak olduğunu düşünüyorum. AngularJS bunu dahili olarak yapar ve burada dokümanları ve diğer konuları okuduktan sonra belirli bir cevap bulamadım.

Anında aramayı nasıl geciktirebileceğime dair herhangi bir işaretçi takdir ediyorum.


1
Tüm json'u init uygulamasında alıyorsunuz ... ve sonra arama filtreniz ikinci kez yazarken verileri almıyor ... zaten var olan modeli filtreliyor. Doğrumuyum?
Maksym

Aşağıdaki cevap işe yaradı mı? Eğer öyleyse, lütfen cevabı kabul edin. Değilse, bana bildirin, daha fazla açıklığa kavuşacağım.
Jason Aden

Hey Jason, cevap için teşekkürler. Kodunuzla oynamaya çalışıyordum ama şansım yok, arama tamamen benim için çalışmıyor.
Braincomb

Boşver, bir şeyleri gözden kaçırdım. Çözümünüz gerçekten işe yarıyor. Teşekkür ederim :)
braincomb

Ng-change konusunda gecikme yapmanızı sağlayan bir direktif sağlayan bu cevaba bir göz atın: stackoverflow.com/questions/21121460/…
Doug

Yanıtlar:


121

(Açısal 1.3 çözümü için aşağıdaki cevaba bakınız.)

Buradaki sorun, model her değiştiğinde aramanın yürütülmesi, yani bir girişteki her tuş eylemi.

Bunu yapmanın daha temiz yolları olurdu, ancak muhtemelen en kolay yol, filtrenin üzerinde çalıştığı Denetleyicinizin içinde tanımlanmış bir $ scope özelliğine sahip olmak için bağlamayı değiştirmek olacaktır. Bu şekilde $ scope değişkeninin ne sıklıkta güncelleneceğini kontrol edebilirsiniz. Bunun gibi bir şey:

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>

Unutmayın ki $ scope. $ Watch on angular-ui bootstrap's modalng-model içinde işe yaramaz
Hendy Irawan

1
Ayrıca tempFilterText değişkeni olmadan da çalışacağını düşünüyorum: $ scope. $ Watch ('searchText', function (val) {if (filterTextTimeout) $ timeout.cancel (filterTextTimeout); filterTextTimeout = $ timeout (function () {$ scope. filterText = val;}, 250); // 250 ms gecikme})
Jos Theeuwen

@JosTheeuwen o zaman sadece kötü uygulama olarak kabul edilen ve katı modda izin verilmeyen küresel bir değişken .
mb21

301

GÜNCELLEME

Şimdi her zamankinden daha kolay (Açısal 1.3), sadece modele bir debounce seçeneği ekleyin.

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

Güncellenmiş dalgıç:
http://plnkr.co/edit/4V13gK

NgModelOptions hakkında dokümanlar:
https://docs.angularjs.org/api/ng/directive/ngModelOptions

Eski yöntem:

İşte açısalın ötesinde bağımlılıkları olmayan başka bir yöntem.

Bir zaman aşımı ayarlamanız ve geçerli dizenizi geçmiş sürümle karşılaştırmanız gerekir, her ikisi de aynıysa aramayı gerçekleştirir.

$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);
});

ve bu sizin görüşünüze girer:

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

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

Zorunlu dalgıç: http://plnkr.co/dAPmwf


2
Benim için kabul edilenden çok daha anlaşılır bir cevap :) Teşekkürler!
OZ_

3
Birden fazla model değişikliğinin birikebileceği ve bu nedenle yinelenen isteklere neden olabileceği bir sorun yok mu? @ JasonAden'in cevabında, daha önce kuyruğa alınan olayları iptal ederek bununla ilgilenir.
Blaskovicz

Teoride, model bir değişiklik yaşar, ancak veriler aynı kalırsa, birden fazla talebe neden olur. Pratikte bunun olduğunu hiç görmedim. Endişeleniyorsanız bu kenar durumunu kontrol etmek için bir bayrak ekleyebilirsiniz.
Josue Alexander Ibarra

Bu açısal 1.3 için çok iyi bir seçim
Marcus W

Burada uyarı: Gönderen veya tetikleyen bir tuşa basma olayınız varsa, değer bağlaması kaldırıldığı için bunu en son model değeri olmadan yapar. Örneğin, 'foo' yazın ve hemen tuşa basıldığında değer yine de boş bir dize olur.
jbodily

34

Açısal 1.3'te bunu yaparım:

HTML:

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

Denetleyici:

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

Temelde myDebouncedFunction(), msgkapsam değişkeni değiştiğinde açısal çalışmasını söylersiniz. Nitelik ng-model-options="{debounce: 1000}"emin olur msgsadece bir saniye kez güncelleyebilirsiniz.


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

Şimdi ng-model-seçenekleri açılma zamanını ayarlayabiliriz ve bulanıklık olduğunda, modelin hemen değiştirilmesi gerekir, aksi takdirde gecikme tamamlanmazsa kaydettikten sonra daha eski bir değere sahip olacaktır.


9

HTML işaretlemesinde keyup / keydown kullananlar için. Bu saat kullanmaz.

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 için geliştirilmiş / kısaltılmış model güncellemeleri: http://jsfiddle.net/lgersman/vPsGb/3/

Sizin durumunuzda jsfiddle kodundaki yönergeyi şu şekilde kullanmaktan başka bir şey yoktur:

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

Temel olarak http://benalman.com/projects/jquery-throttle-debounce-plugin/ komutunu kullanan "ng-ampere-debounce" adlı tek bir açısal yönergeden oluşan küçük bir kod parçası , herhangi bir dom öğesine eklenebilen parçasıdır. Yönerge, ekli olay işleyicilerini yeniden sıralayarak olayları ne zaman kısacağını denetleyebilir.

Kısma / açma * model açısal güncellemeleri * açısal olay işleyici ng- [olay] * jquery olay işleyicileri için kullanabilirsiniz

Bir göz atın: http://jsfiddle.net/lgersman/vPsGb/3/

Yönerge Orangevolt Amper çerçevesinin bir parçası olacaktır ( https://github.com/lgersman/jquery.orangevolt-ampere ).


6

Yalnızca buraya yönlendirilen kullanıcılar için:

Bahsedildiği gibi içinde Angular 1.3kullanabileceğiniz -model seçenekleri ng niteliğini:

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

5

Bu sorunu çözmenin en iyi yolunun Ben Alman eklentisi jQuery throttle / debounce kullanmak olduğuna inanıyorum . Bence formunuzdaki her alanın olayını ertelemeye gerek yok.

Sadece $ kapsamınızı sarın. $ Watch taşıma fonksiyonu $ .debounce içinde şöyle:

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

Bunu $ kapsamına almanız gerekecek. $ Apply
Aakil Fernandes

3

Başka bir çözüm, model güncellemesine bir gecikme işlevselliği eklemektir. Basit direktif bir hile yapıyor gibi görünüyor:

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);
            });
        }
    };
});

Kullanımı:

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

Sadece kullanmak Yani delayed-modelyerine ng-modelve tanımlamak istenirse data-delay.

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


Hey! nasıl model: '=delayedModel'çalıştığını açıklayabilir misiniz ? Yoksa beni bulabileceğim bir bağlantıya yönlendirebilir misin?
Akash Agrawal

@AkashAgrawal İki yönlü veri bağlama özelliği. Bu konuda buradan okuyun docs.angularjs.org/api/ng.$compile
dfsq

1
@dfsq ng-change kullanıyordum ve metinde her değişiklik olduğunda tetikleme yapıyordum. Ancak bir direktif tanımlandığında bunu kullanamam. element.on('change')yalnızca bulanıklıkta tetiklenir. (1) Etrafta bir çalışma var mı? (2) metin değişikliği denetleyicinin bir işlevi nasıl çağırılır?
Vyas Rao

0

Temelde yaptığı şey, gerçek ng-modelini direktifte izlediğim özel bir özniteliğe bağlamak, daha sonra bir debounce servisi kullanarak direktif özniteliğimi güncellemek, yani kullanıcı değişkenini izlemek ng modeli yerine kaldırma modeline bağlanır.

.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);
  }
};
});

Kullanımı:

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

Ve denetleyicide:

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

Jsfiddle'da demo: http://jsfiddle.net/6K7Kd/37/

$ debounce hizmeti şu adreste bulunabilir: http://jsfiddle.net/Warspawn/6K7Kd/

En sonunda esinlenen Bağlayıcı direktif http://jsfiddle.net/fctZH/12/


0

Açısal 1.3, ng-model-seçenekleri geri dönecek, ancak o zamana kadar, Josue Ibarra'nın dediği gibi bir zamanlayıcı kullanmanız gerekiyor. Ancak, kodunda her tuşa basıldığında bir zamanlayıcı başlatır. Ayrıca, Angular'da $ timeout veya setTimeout'un sonunda $ Apply kullanmak zorundayken setTimeout kullanıyor.


0

Neden herkes saati kullanmak istiyor? Ayrıca bir işlev de kullanabilirsiniz:

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

Ben burada en kolay yolu json önceden yüklemek veya bir kez yüklemek olduğunu düşünüyorum $dirtyve sonra filtre arama gerisini halledecektir. Bu, ekstra http çağrılarını ve önceden yüklenmiş verilerle çok daha hızlı bir şekilde kaydedilmesini sağlayacaktır. Bellek acıtacak, ama buna değer.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.