20

我遇到了这个问题,我不知道如何解决它。我在我的页面中使用了ui-select 多选。首先,向获取数据的 url 发出 http.get 请求,然后填充 ui-select 选项。数据很大 - 数据长度为 2100。此数据将显示为选项。(数据在页面加载过程中一开始就获取,并存储在一个数组中)

但问题是每次我点击多选选择一个选项时,填充列表需要 4-5 秒,页面变得非常慢。我该怎么做才能减少这个时间?

选择数据存储在数组中,数据类型是字符串数组。

  <ui-select multiple ng-model="selectedFields.name"  style="width: 100%;">
    <ui-select-match placeholder="Select fields...">{{$item}}</ui-select-match>
    <ui-select-choices repeat="fields in availableFields | filter:$select.search">
      {{fields}}
    </ui-select-choices>
  </ui-select>

在控制器中,

$scope.selectedFields = {};
$scope.selectedFields.name = [];

$scope.init = function() {

    $http.get(url)
        .success( function(response, status, headers, config) {
            availableFields = response;
        })
        .error( function(err) {
        });
};

$scope.init();

如果不是这种方式,是否还有其他我可以使用的选项/选择不会延迟显示选择选项?

4

4 回答 4

17

这是 ui-select 中的一个已知问题。我尝试了以下方法,都有效

1)有一个解决方法 - 使用

| limitTo: 100

这将选项显示限制为 100,但可以选择所有选项。查看此线程以获取更多详细信息。

2)由于某些时候,需要在选项中显示整个列表,1)不是一个可行的选项。我使用了一个不同的库 - selectize.js。这是页面中给出的一个plunker演示

于 2015-07-16T04:54:46.210 回答
8

这是装饰uiSelectChoices指令的完整解决方案。

随着用户滚动,项目会逐渐填充。

还负责卷轴中的搜索。

也适用于所有值position={auto, up, down}

例子

    <ui-select-choices 
         position="up" 
         all-choices="ctrl.allTenThousandItems"  
         refresh-delay="0"
         repeat="person in $select.pageOptions.people | propsFilter: {name: $select.search, age: $select.search} ">
      <div ng-bind-html="person.name | highlight: $select.search"></div>
      <small>
         email: {{person.email}}
         age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
       </small>
   </ui-select-choices>

与v0.19.5 一起使用 Plnkr

该指令

  app.directive('uiSelectChoices', ['$timeout', '$parse', '$compile', '$document', '$filter', function($timeout, $parse, $compile, $document, $filter) {
    return function(scope, elm, attr) {
    var raw = elm[0];
    var scrollCompleted = true;
    if (!attr.allChoices) {
      throw new Error('ief:ui-select: Attribute all-choices is required in  ui-select-choices so that we can handle  pagination.');
    }

    scope.pagingOptions = {
      allOptions: scope.$eval(attr.allChoices)
    };

    attr.refresh = 'addMoreItems()';
    var refreshCallBack = $parse(attr.refresh);
    elm.bind('scroll', function(event) {
      var remainingHeight = raw.offsetHeight - raw.scrollHeight;
      var scrollTop = raw.scrollTop;
      var percent = Math.abs((scrollTop / remainingHeight) * 100);

      if (percent >= 80) {
        if (scrollCompleted) {
          scrollCompleted = false;
          event.preventDefault();
          event.stopPropagation();
          var callback = function() {
            scope.addingMore = true;
            refreshCallBack(scope, {
              $event: event
            });
            scrollCompleted = true;

          };
          $timeout(callback, 100);
        }
      }
    });

    var closeDestroyer = scope.$on('uis:close', function() {
      var pagingOptions = scope.$select.pagingOptions || {};
      pagingOptions.filteredItems = undefined;
      pagingOptions.page = 0;
    });

    scope.addMoreItems = function(doneCalBack) {
      console.log('new addMoreItems');
      var $select = scope.$select;
      var allItems = scope.pagingOptions.allOptions;
      var moreItems = [];
      var itemsThreshold = 100;
      var search = $select.search;

      var pagingOptions = $select.pagingOptions = $select.pagingOptions || {
        page: 0,
        pageSize: 20,
        items: $select.items
      };

      if (pagingOptions.page === 0) {
        pagingOptions.items.length = 0;
      }
      if (!pagingOptions.originalAllItems) {
        pagingOptions.originalAllItems = scope.pagingOptions.allOptions;
      }
      console.log('search term=' + search);
      console.log('prev search term=' + pagingOptions.prevSearch);
      var searchDidNotChange = search && pagingOptions.prevSearch && search == pagingOptions.prevSearch;
      console.log('isSearchChanged=' + searchDidNotChange);
      if (pagingOptions.filteredItems && searchDidNotChange) {
        allItems = pagingOptions.filteredItems;
      }
      pagingOptions.prevSearch = search;
      if (search && search.length > 0 && pagingOptions.items.length < allItems.length && !searchDidNotChange) {
        //search


        if (!pagingOptions.filteredItems) {
          //console.log('previous ' + pagingOptions.filteredItems);
        }

        pagingOptions.filteredItems = undefined;
        moreItems = $filter('filter')(pagingOptions.originalAllItems, search);
        //if filtered items are too many scrolling should occur for filtered items
        if (moreItems.length > itemsThreshold) {
          if (!pagingOptions.filteredItems) {
            pagingOptions.page = 0;
            pagingOptions.items.length = 0;
          } else {

          }
          pagingOptions.page = 0;
          pagingOptions.items.length = 0;
          allItems = pagingOptions.filteredItems = moreItems;

        } else {
          allItems = moreItems;
          pagingOptions.items.length = 0;
          pagingOptions.filteredItems = undefined;
        }


      } else {
        console.log('plain paging');
      }
      pagingOptions.page++;
      if (pagingOptions.page * pagingOptions.pageSize < allItems.length) {
        moreItems = allItems.slice(pagingOptions.items.length, pagingOptions.page * pagingOptions.pageSize);
      }

      for (var k = 0; k < moreItems.length; k++) {
        pagingOptions.items.push(moreItems[k]);
      }

      scope.calculateDropdownPos();
      scope.$broadcast('uis:refresh');
      if (doneCalBack) doneCalBack();
    };
    scope.$on('$destroy', function() {
      elm.off('scroll');
      closeDestroyer();
    });
  };
}]);
于 2016-06-28T13:18:07.520 回答
4

因为我不能发表评论(没有足够的代表)我写这个作为答案,很抱歉这不是问题的答案。

@bhantol 我将以下代码行更改为您的解决方案,到目前为止这对我来说非常有效

for (var k = 0; k < moreItems.length; k++) {
  pagingOptions.items.push(moreItems[k]);
}

for (var k = 0; k < moreItems.length; k++) {
  if (pagingOptions.items.indexOf(moreItems[k]) == -1){
    pagingOptions.items.push(moreItems[k]);
  }
}

如果用户开始编写过滤器然后将其删除,这可以防止出现重复的项目。

另外我刚刚发现如果列表小于 20 项,它将不起作用,所以我更改了:

if (pagingOptions.page * pagingOptions.pageSize < allItems.length) {
  moreItems = allItems.slice(pagingOptions.items.length, pagingOptions.page * pagingOptions.pageSize);
}

至:

if (pagingOptions.page * pagingOptions.pageSize < allItems.length) {
  moreItems = allItems.slice(pagingOptions.items.length, pagingOptions.page * pagingOptions.pageSize);
}
else{ moreItems = allItems;}

也许这会以某种方式对您有所帮助,并再次为没有回答问题而感到抱歉。

于 2016-07-13T09:04:09.747 回答
4

如前所述,ui-select 存在很多性能问题,但有一个解决限制问题的方法。

如果您遵循 akashrajkn 的方法,那么您会注意到它实际上会删除重要的数据,因为它一次只会渲染 100 个。有一个已通过单元测试的修复程序,可以在此处的线程上找到:

https://github.com/angular-ui/ui-select/pull/716

基本上,如果您在本地存储 javascript 文件,那么您可以调整未缩小的版本。您需要做的就是实施他在拉取请求中所做的更改,它应该会有所帮助。为了应用限制因素,请看下面的修改示例:

<ui-select multiple ng-model="selectedFields.name" limit = "10"  style="width: 100%;">
    <ui-select-match placeholder="Select fields...">{{$item}}</ui-select-match>
    <ui-select-choices repeat="fields in availableFields | filter:$select.search | limitTo:$select.limit ">
      {{fields}}
    </ui-select-choices>
</ui-select>

以上将限制您在下拉列表中的数据,同时还保持所需的一致性级别。

于 2015-07-16T18:41:22.617 回答