javascript - 当服务器未按照请求的顺序返回数据时,在 Angular 中键入时进行搜索以显示准确的结果?

标签 javascript angularjs ajax html asp.net-ajax

所以我正在为客户建立一个网站。在我们的搜索页面上,我正在观察搜索栏,每次击键都会触发一个新的搜索请求,因此它会在您键入时更新结果。我的问题是,当我非常快地输入搜索词时,服务器可能会“打嗝”,并且不会按照我请求的顺序返回结果。这只会导致我的标记的某些部分绑定(bind)到返回的数据以显示错误的值。例如。如果我输入“视频”这个词。 99% 的情况下我都会得到正确的结果,并且所有标签看起来都是正确的。然而,有 1% 的情况下,我搜索“视频”后会返回“视频”搜索,这导致它显示错误的结果和错误的总数。不幸的是,由于保密协议(protocol),我无法链接现场演示,但我可以发布使其运行的代码。我们正处于生产的后期阶段,因此尽快使其正常运行比使用最佳实践更重要。 html 的一些片段:

<h1>Search</h1>
                <p ng-cloak class="results-message fade-in">
                  <span ng-if="items.loaded &amp;&amp; term">{{total}} results for "{{term}}"  </span>        
                  <span ng-if="!total &amp;&amp; !term">No query was entered </span>
                  <span ng-if="!items.loaded &amp;&amp; term">Searching...</span>
                </p>
                <div class="search-container">         
                    <input type="search" name="term" value="asd" ng-init="term='asd'" ng-model="term" placeholder="Enter your search term" ng-keydown="$event.keyCode == 13 ? $event.preventDefault() : 0;" class="search">
                    <input type="image" name="submit" src="/_res/astoncarter/img/images/search-icon.gif" alt="Submit">
                </div>
            </div>

<ul ng-cloak ng-class="{loading: loading}" class="results">
               <li ng-if="items.loaded && !items.length && term" class="fade-in">
                    <h2>No results were found for {{term}}</h2>
                </li>
                <li ng-if="!term" class="fade-in">
                    <p class="text-center">Please enter a search query</p>
                </li>
                <li ng-repeat="item in items" class="fade-in">
                    <h2><a href="{{item.url}}">{{item.title}}</a></h2>
                    <p>{{item.teaser}}</p>
                </li>
            </ul>
        </div>
    </div>
</div>
<p ng-if="count == 10" class="load-more"><a href="" ng-if="!loading" ng-click="loadQuery()" class="btn">Load More</a><a href="" ng-if="loading" disabled class="btn"><i class="fa fa-spinner fa-spin"> </i> LOAD</a></p>

JS:

app.controller('ListingCtrl', function ($scope, $element, $timeout, acAPIservice, $location, $interval) {
  // Grabbing and using the attribute from the dom is not the Angular way 
  // but a compromize if we want BE developers to be able to set the API
  // Other options would be to set a global variable to be output and used
  $scope.apiUrl = $element.attr('data-api-url');
  $scope.pageSize = $element.attr('data-page-size');
  $scope.language = $element.attr('data-language');
  $scope.timer = 3; 

  $scope.term = undefined;
  $scope.items = [];
  $scope.start = 0;
  $scope.end = 0;
  $scope.total = 0;
  $scope.loading = false;


 // main worker to get data
 // lists that need data should call this on ng-init
 $scope.loadQuery = function(){
   $scope.loading = true;

  var payload = {
    pageSize : $scope.pageSize,
    start : $scope.end+1,
    language : $scope.language
  };
  if($scope.term) {
    payload.term = $scope.term
  }

  acAPIservice.getSearch($scope.apiUrl, payload) //this just hits a factory with the url and payload and returns the data object
  .success(function (data) {
    $scope.timer = 3; //reset the timer
    $scope.items = $scope.items.concat(data.results);
    $scope.start = data.start;
    $scope.end = data.end;
    $scope.total = data.total;
    $scope.loading = false;
    $scope.items.loaded = true;
    $scope.count = data.count;
  })
  .error(function(data, status, headers, config){      
    $scope.items = [];
    $scope.items.loaded = true;
  });
};

// cheating a bit here. We let the ng-init attribute for term trigger 
// the first batch. Also grabs new batches when term is updated
$scope.$watch('term', function(newValue, oldValue) {
// reset everything
$scope.items = [];
$scope.start = 0;
$scope.end = 0;
$scope.total = 0;
// if we still have a search term go get it
if($scope.term){
  $scope.loadQuery();

  //because you can't turn off async in angular we need to set a timer that gets reset every time you load a query.     After 1 second we recall to make sure our total reflects the accurate num
  var promise = $interval( function(){ 
      $scope.timer = $scope.timer-1;
      if($scope.timer === 0){ //if the timer runs out it means that no terms have been entered in 1 second, which we will then cancel the interval and and do one last load
        $interval.cancel(promise);
        $scope.loadQuery();
      }
  }, 500);
}
  });
});

它总是显示正确的搜索词,但是它会显示最后返回的 {{total}},并且看起来最后 2 个返回的结果被附加在一起。

我只是尝试实现间隔在一秒过去后“刷新”结果而无需用户输入,但有时服务器可能需要超过 1 秒的时间,这意味着它仍然会使用错误的信息进行更新。如果您需要我解决问题,请询问我任何问题。有时我很难解释我的问题。也对格式表示抱歉:(

最佳答案

只需返回在结果中搜索的文本即可。当 Promise 被解析时,如果返回的文本不等于当前文本,则从函数中返回。

acAPIservice.getSearch($scope.apiUrl, payload)
  .success(function (data) {
    if (data.term !== $scope.term) {
      return;
    }

    $scope.timer = 3; //reset the timer
    $scope.items = $scope.items.concat(data.results);
    $scope.start = data.start;
    $scope.end = data.end;
    $scope.total = data.total;
    $scope.loading = false;
    $scope.items.loaded = true;
    $scope.count = data.count;
  })
  .error(function(data, status, headers, config){      
    $scope.items = [];
    $scope.items.loaded = true;
  });

关于javascript - 当服务器未按照请求的顺序返回数据时,在 Angular 中键入时进行搜索以显示准确的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36434475/

相关文章:

javascript - 为什么我的 PreventDefault 在 Chrome 浏览器 jquery 中不起作用?

javascript - 数组中的 JSON 对象数组在 javascript 中查找和替换

jquery - 在 header 中设置接受语言

c# - 使用 JQuery/AJAX 将 JSON 发送到 Web 服务时出现 "Object reference not set to an instance of an object"

javascript - 我想将选择框的 ID 传递给 Angular JS 中的函数

javascript - for (x in y) VS for (i = 0; i < y.length; i++) 其中 x 是对象数组

javascript - 将 .txt 文件中的文本拉入 &lt;input&gt; 值 HTML

javascript - angularjs 根据给定数组重新排序 ng 重复结果

angularjs - 为什么 ngModel.$setViewValue(...) 不起作用

jquery - Ajax 添加 HTML 不继承页面样式 - jQuery Mobile