Làm cách nào để đặt độ trễ cho tìm kiếm tức thì của AngularJS?


147

Tôi có một vấn đề hiệu suất mà tôi dường như không thể giải quyết. Tôi có một tìm kiếm tức thì nhưng nó hơi lag, vì nó bắt đầu tìm kiếm trên mỗi cái 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>

Dữ liệu JSON thậm chí không lớn đến mức chỉ 300KB, tôi nghĩ rằng những gì tôi cần thực hiện là đặt độ trễ ~ 1 giây trên tìm kiếm để chờ người dùng nhập xong, thay vì thực hiện hành động trên mỗi lần nhấn phím. AngularJS thực hiện điều này trong nội bộ, và sau khi đọc tài liệu và các chủ đề khác ở đây, tôi không thể tìm thấy câu trả lời cụ thể.

Tôi sẽ đánh giá cao bất kỳ con trỏ nào về cách tôi có thể trì hoãn việc tìm kiếm tức thì.


1
Bạn đang nhận được tất cả json trên ứng dụng init ... và sau đó bộ lọc tìm kiếm của bạn sẽ không nhận được dữ liệu lần thứ hai khi nhập ... đó là mô hình đã có sẵn. Tôi có đúng không?
Maksym

Câu trả lời dưới đây có hiệu quả không? Nếu vậy, xin vui lòng chấp nhận câu trả lời. Nếu không, hãy cho tôi biết và tôi sẽ làm rõ thêm.
Jason Aden

Này Jason, cảm ơn vì đã trả lời. Tôi đã cố gắng chơi xung quanh với mã của bạn nhưng không có may mắn, tìm kiếm ngừng hoạt động hoàn toàn cho tôi.
Braincomb

Nevermind, đó là xấu của tôi, tôi bỏ qua một cái gì đó. Giải pháp của bạn thực sự hoạt động. Cảm ơn bạn :)
Braincomb

Hãy xem câu trả lời này tại đây, cung cấp một chỉ thị cho phép bạn đặt độ trễ cho thay đổi ng: stackoverflow.com/questions/21121460/ Kẻ
Doug

Câu trả lời:


121

(Xem câu trả lời dưới đây cho giải pháp Angular 1.3.)

Vấn đề ở đây là việc tìm kiếm sẽ thực thi mỗi khi mô hình thay đổi, đó là mọi hành động gõ phím trên đầu vào.

Sẽ có những cách sạch hơn để làm điều này, nhưng có lẽ cách dễ nhất là chuyển đổi liên kết để bạn có thuộc tính $ scope được xác định bên trong Bộ điều khiển mà bộ lọc của bạn hoạt động. Bằng cách đó bạn có thể kiểm soát tần suất của biến $ scope đó được cập nhật. Một cái gì đó như thế này:

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>

Lưu ý rằng $ scope. $ Xem trên một ng-modeltác phẩm sẽ không hoạt động trong phương thức của bootstrap angular-ui
Hendy IINA

1
Tôi nghĩ rằng nó cũng sẽ hoạt động mà không có biến tempFilterText: $ scope. $ Watch ('searchText', function (val) {if (filterTextTimeout) $ timeout.cattery (filterTextTimeout); filterTextTimeout = $ timeout (function () {$ scope. bộ lọc lọc = val;}, 250); // trì hoãn 250 ms})
Jos Theeuwen

@JosTheeuwen sau đó chỉ đơn giản là một biến toàn cục được coi là thực tiễn xấu và không được phép trong chế độ nghiêm ngặt .
mb21

301

CẬP NHẬT

Bây giờ thì dễ dàng hơn bao giờ hết (Angular 1.3), chỉ cần thêm tùy chọn gỡ lỗi trên mô hình.

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

Cập nhật plunker:
http://plnkr.co/edit/4V13gK

Tài liệu về ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions

Phương pháp cũ:

Đây là một phương pháp khác không có sự phụ thuộc ngoài chính góc cạnh.

Bạn cần đặt thời gian chờ và so sánh chuỗi hiện tại của bạn với phiên bản trước, nếu cả hai đều giống nhau thì nó sẽ thực hiện tìm kiếm.

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

và điều này đi vào quan điểm của bạn:

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

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

Plunker bắt buộc: http://plnkr.co/dAPmwf


2
Đối với tôi đó là câu trả lời dễ hiểu hơn nhiều so với chấp nhận :) Cảm ơn!
OZ_

3
Không có vấn đề trong đó nhiều thay đổi mô hình có thể chồng lên nhau, do đó gây ra các yêu cầu trùng lặp? Trong câu trả lời của @ JasonAden, anh ấy quan tâm đến điều đó bằng cách hủy các sự kiện được xếp hàng trước đó.
Blaskovicz

Về lý thuyết, nếu mô hình trải qua một sự thay đổi, nhưng dữ liệu vẫn giữ nguyên, nó sẽ gây ra nhiều yêu cầu. Trong thực tế tôi chưa bao giờ thấy nó xảy ra. Bạn có thể thêm một cờ để kiểm tra trường hợp cạnh đó nếu bạn lo lắng.
Josue Alexander Ibarra

Đây là sự lựa chọn ưu việt cho góc 1.3
Marcus W

Cảnh báo ở đây: Nếu bạn có một sự kiện nhấn phím gửi hoặc kích hoạt, nó sẽ làm như vậy mà không có giá trị mô hình mới nhất kể từ khi ràng buộc giá trị được công bố. ví dụ: gõ 'foo' và trên phím bấm trả về ngay lập tức, giá trị sẽ vẫn là một chuỗi trống.
vui vẻ vào

34

Trong Angular 1.3 tôi sẽ làm điều này:

HTML:

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

Điều khiển:

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

Về cơ bản, bạn đang bảo góc chạy myDebouncedFunction(), khi msgbiến phạm vi thay đổi. Thuộc tính ng-model-options="{debounce: 1000}"đảm bảo rằng msgchỉ có thể cập nhật một lần một giây.


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

Bây giờ chúng ta có thể thiết lập gỡ lỗi tùy chọn ng-model-model theo thời gian và khi mờ, mô hình cần phải được thay đổi ngay lập tức nếu không lưu nó sẽ có giá trị cũ hơn nếu độ trễ không được hoàn thành.


9

Đối với những người sử dụng keyup / keydown trong đánh dấu HTML. Điều này không sử dụng đồng hồ.

Mã não

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

Cập nhật mô hình đã được công bố / điều chỉnh cho angularjs: http://jsfiddle.net/lgersman/vPsGb/3/

Trong trường hợp của bạn, không có gì để làm hơn là sử dụng lệnh trong mã jsfiddle như thế này:

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

Về cơ bản, đây là một đoạn mã nhỏ bao gồm một lệnh góc duy nhất có tên là "ng-ampere-debounce" sử dụng http://benalman.com/projects/jquery-thrption-debounce-plugin/ có thể được gắn vào bất kỳ phần tử dom nào. Lệnh này sắp xếp lại các trình xử lý sự kiện đính kèm để nó có thể kiểm soát thời điểm điều tiết các sự kiện.

Bạn có thể sử dụng nó để điều chỉnh / gỡ lỗi * cập nhật góc mô hình * xử lý sự kiện góc ng- [event] * xử lý sự kiện jquery

Hãy xem: http://jsfiddle.net/lgersman/vPsGb/3/

Lệnh này sẽ là một phần của khung Orangevolt Ampere ( https://github.com/lgersman/jquery.orangevolt-ampere ).


6

Chỉ dành cho người dùng chuyển hướng ở đây:

Như đã giới thiệu, Angular 1.3bạn có thể sử dụng thuộc tính ng-model-Options :

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

5

Tôi tin rằng cách tốt nhất để giải quyết vấn đề này là bằng cách sử dụng trình điều khiển / gỡ lỗi plugin của Ben Alman . Theo tôi, không cần phải trì hoãn các sự kiện của từng lĩnh vực trong biểu mẫu của bạn.

Chỉ cần bọc chức năng xử lý đồng hồ $ scope của bạn trong $ .debounce như thế này:

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

Bạn sẽ cần bọc cái này trong phạm vi $. $
Áp

3

Một giải pháp khác là thêm chức năng trì hoãn để cập nhật mô hình. Lệnh đơn giản dường như thực hiện một mẹo:

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

Sử dụng:

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

Vì vậy, bạn chỉ cần sử dụng delayed-modelthay thế ng-modelvà xác định mong muốn data-delay.

Bản trình diễn: http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview


Chào! bạn có thể giải thích làm thế nào model: '=delayedModel'là làm việc? Hoặc bạn có thể chỉ cho tôi một liên kết nơi tôi có thể tìm thấy nó?
Akash Agrawal

@AkashAgrawal Đó là một ràng buộc dữ liệu hai chiều. Đọc về nó ở đây docs.angularjs.org/api/ng.$compile
dfsq

1
@dfsq Tôi đã sử dụng ng-change và nó được sử dụng để kích hoạt bất cứ khi nào có thay đổi trong văn bản. Nhưng tôi không thể sử dụng nó khi một lệnh được xác định. element.on('change')kích hoạt chỉ trên mờ. (1) Có một công việc xung quanh? (2) làm thế nào để gọi một chức năng của bộ điều khiển khi thay đổi văn bản?
Vyas Rao

0

Tôi đã giải quyết vấn đề này bằng một chỉ thị về cơ bản những gì nó làm là liên kết mô hình ng thực trên một thuộc tính đặc biệt mà tôi xem trong chỉ thị, sau đó sử dụng dịch vụ gỡ lỗi Tôi cập nhật thuộc tính chỉ thị của mình, vì vậy người dùng theo dõi biến đó anh ấy liên kết với mô hình gỡ lỗi thay vì mô hình ng.

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

Sử dụng:

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

Và trong bộ điều khiển:

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

Bản trình diễn trong jsfiddle: http://jsfiddle.net/6K7Kd/37/

dịch vụ $ debounce có thể được tìm thấy ở đây: http://jsfiddle.net/Warspawn/6K7Kd/

Lấy cảm hứng từ chỉ thị cuối cùngBind http://jsfiddle.net/fctZH/12/


0

Angular 1.3 sẽ có bản phát hành tùy chọn ng-model-model, nhưng cho đến lúc đó, bạn phải sử dụng bộ đếm thời gian như Josue Ibarra nói. Tuy nhiên, trong mã của mình, anh ta khởi chạy một bộ đếm thời gian trên mỗi lần nhấn phím. Ngoài ra, anh ta đang sử dụng setTimeout, khi ở Angular, người ta phải sử dụng $ timeout hoặc sử dụng $ áp dụng vào cuối setTimeout.


0

Tại sao mọi người đều muốn sử dụng đồng hồ? Bạn cũng có thể sử dụng một chức năng:

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

Tôi nghĩ cách dễ nhất ở đây là tải trước json hoặc tải nó một lần $dirtyvà sau đó tìm kiếm bộ lọc sẽ giải quyết phần còn lại. Điều này sẽ giúp bạn tiết kiệm các cuộc gọi http thêm và nhanh hơn nhiều với dữ liệu được tải sẵn. Ký ức sẽ đau, nhưng giá trị của nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.