ui-select multiselect is very slow in displaying the choices

你。 提交于 2019-12-02 18:15:31

This is a known issue in ui-select. I tried the following ways, both work

1) There is a workaround for this - use

| limitTo: 100

This limits the choice display to 100 but all the choices can be selected. Look at this thread for more details.

2) Since some of the time, there is a need to display the entire list in the choices, 1) is not a viable option. I used a different library - selectize.js. Here's a plunker demo given in the page

Here is complete solution that decorates uiSelectChoices directive.

Items are populated progressively as the user scrolls.

Also takes care of searches in the scrolls.

Also works for all values of position={auto, up, down}

Example

    <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>

Working Plnkr Also with With v0.19.5

The directive

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

As stated, ui-select is having quite a few performance issues, but there is a workaround for the limit issue.

If you follow akashrajkn's approach then you will notice that it will actually cut out important pieces of data because it will only render 100 at a time. There is a fix that has passed the unit tests and it can be found on the thread here:

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

Basically, if you are storing the javascript file locally, then you can adjust the unminified version. All you need to do is implement the changes he made in the pull request and it should help out significantly. In order to apply the limiting factor, take a look at the below, modified example:

<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>

The above will limit your data in the drop down while also maintaining the level of consistency needed.

Because I cannot leave a comment (not enough rep) I write this as an answer and I am sorry it is no answer for the problem.

@bhantol I changed the following line of code to your solution which is working perfectly for me so far

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

This prevents duplicated items from showing up if the user is starting to write a filter and then deletes it.

Also I just figured out that if the list is smaller than 20 items it will not work so I changed:

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

to:

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

Maybe this will help you somehow and sorry again for not answering the question.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!