angular-ui / ui-select

AngularJS-native version of Select2 and Selectize
MIT License
3.26k stars 1.81k forks source link

big array performance issue #389

Open masscrx opened 9 years ago

masscrx commented 9 years ago

Hi, I have form for customer information, with three fields which use ui-select, every is for selecting country (country array length is 251) when countries array is loaded for ui-select it causes some lag on the page with this data, I've saw that there are some issues open with pagination for far future so now I can resolve my problem by adding checking for minimum input length for example 3 characters, than pagination is made on backend, but when customer is loaded with such information like $scope.customer.country_id I need to load all countries before, so solution with minium input length for 3 characters can't be applied in that case. Some ideas how to achieve that? I was trying to reduce watchers by adding :: to ui-select's expressions (one time binding - Angular 1.3 feauture) but with no result.

brenovieira commented 9 years ago

Yeah I saw it here, but I didn't see an option to disable tagging.

I have a list of airports and the user must select one of them, not create a new one.

Edit: Actually contact-chips may do this:

extension chips

I'll take a look later today. That could work for me. Thanks.

facesea commented 8 years ago

i solved same problem, when i annotation a line code of select.js: qq20151217-1 2x

ssljivic commented 8 years ago

+1

simkessy commented 8 years ago

Did anyone ever solve this? I have a list of 700 items and I'm getting complaints about performance with no clue how to go about solving this.

simkessy commented 8 years ago

@dmccown1500 is there a way to use virtual repeat from ngmaterial with ui-select to fix the performance issues

dmccown1500 commented 8 years ago

@simkessy I never tried I moved to a different project where the library was not in use so it kind of fell off my radar. Seems like it might work?

AdamGerthel commented 8 years ago

I just moved from oi-select to ui-select because oi-select doesn't support using scope variables to populate its attributes for some reason. Took me a good 3-4 hours to re-theme from oi to ui and rewrite some code.

And now this... gah!

simkessy commented 8 years ago

@AdamGerthel, this isn't a fix exactly, but I was able to help my performance by introducing a min-length and using the refresh directives built in. Essentially, I would only populate the dropdown when the user entered at least 2-3 characters.

Pros: Significant performance improvement, instead of rendering 700 items I'm only showing a subset Cons: You don't get a full list of values until you enter something

AdamGerthel commented 8 years ago

@simkessy thank you! It worked wonders. I followed this approach: https://github.com/angular-ui/ui-select/issues/88#issuecomment-179916133

stringbeans commented 8 years ago

@dmccown1500 thanks for providing your solution with infinite scroll! Very cool stuff :) Unfortunately it doesn't seem to be working with the latest version of ngInfiniteScroll as it attaches by default to the window and not the container. I tried using the exact copy of infinite-scroll you used in your plunkr demo and noticed that ui-select would redraw itself after increasing the limit dynamically, thus each time you scroll and it adds more items it skips back to the top of the list again.

were you able to get your solution to work with the latest versions of infinite scroll and ui-select (ie. 0.14.x or 0.16.0) ?

dmccown1500 commented 8 years ago

@stringbeans I left the project where I was using ui-select so I have not kept that up to date. So if you need a feature from one of the newer versions then you are kind of out of luck. Sorry!

SunnyTam commented 8 years ago

Is there any fix on this issue?

user378230 commented 8 years ago

@TravisTam feel free to submit any PRs you feel could improve performance.

I'll be happy to review :smiley:

edriang commented 8 years ago

Hi, wanted to share what I did in my project..

First commented the same line that mentioned @facesea: // .attr('ng-if', '$select.open'); //Prevent unnecessary watches when dropdown is closed

But also the major problem with ui-select is using it with array | filter syntax. As explained in this page https://toddmotto.com/use-controller-filters-to-prevent-digest-performance-issues/ using filter in html causes big performance issues because it gets called on every digest cycle.

Then, if you don't use filter in view, .attr('ng-if', '$select.open'); it's no longer necessary.

Finally I had to modify select.js to emit an "on-filter" event when $select.search changes, so I could update the source array when user types something.

$scope.$watch('$select.search', function(search){
        $scope.$emit('on-filter', search);
});

Hope this tips helps to improve the library.

evenicoulddoit commented 8 years ago

@stringbeans you're right, the latest infinite scroll plugin uses the window by default, but it does allow you to modify the container. The scope properties are:

{
  infiniteScroll: '&',
  infiniteScrollContainer: '=',
  infiniteScrollDistance: '=',
  infiniteScrollDisabled: '=',
  infiniteScrollUseDocumentBottom: '=',
  infiniteScrollListenForEvent: '@'
}

However... I've tried in vain to set it up to work correctly. You need to pass a selector to infiniteScrollContainer, and neither using .ui-select-choices-group nor #ui-select-choice-0 (which it looks like the old version was using automatically as the container) works.

jocluz commented 8 years ago

any news on this issue?

user378230 commented 8 years ago

@jocluz this issue is up to date with all info posted so far, what further news do you require?

Feel free to submit any PRs you feel could improve performance I'll be happy to review 😃

benwigley commented 8 years ago

I also have found a solution similar to some above, but in to my eyes was a bit more straight forward.

In the view

<ui-select-choices repeat="school in filterSchools($select.search)">
  <div ng-bind-html="school.name | highlight: $select.search"></div>
</ui-select-choices>

In the controller. I have a list of 3,000+ schools, and I have no need for the user to scroll through them all at once so I return an empty array when there is no search term. When there is a search term I limit the array to 30 items.

$scope.filterSchools = (search) ->
    return [] if !search
    limit = 30
    index = 0
    searchRegex = new RegExp(search, 'gi') # gi = global and case insensitive
    return $scope.schools.filter (school) ->
      if school.name.match(searchRegex) && index < limit
        index += 1
        return true

If you want the dropdown to look nice (invisible) when there are no items in the array then you might want to edit the css of . ui-select-dropdown and remove the border (I am using the selectize theme so this might vary depending on the theme).

jacek213 commented 8 years ago

@evenicoulddoit I'm also having a hard time integring ng-infinite-scroll. I've tried to add infinite scroll attributes to ui-select-choices like:

<ui-select-choices repeat="user.id as user in mmCtrl.users | filter: $select.search"
                   refresh="mmCtrl.onNextPage($select)"
                   refresh-delay="300"

                   infinite-scroll="mmCtrl.onNextPage($select, $event, true)"
                   infinite-scroll-distance='0'
                   infinite-scroll-container="#ui-select-choices-0"
                   infinite-scroll-parent="true"
                   infinite-scroll-use-document-bottom="false"
                   >

No success so far - probably because the container to which I'm pointing is not really a parent of ui-select-choices but it's the element to be scrolled itself.

Remiz commented 7 years ago

@benwigley I tried something similar to you, but in my case I have 100k records in a list... The dropdown gets populated really fast and it seems to work. I'm using a multiple select and when I select more than one object it transform all the objects to the one selected last... I think the binding can't work in my case but if I bind to the full list the performances are terrible. Not sure what to do.

jacek213 commented 7 years ago

@Remiz if you want to stick to ui-select, hooking up infinite scroll seems like the only option imho.

7899 commented 7 years ago

Infinite scroll solution is misleading. the problem still persists, if you keep scrolling down the performance will start to degrade again as the list grows and grows.

You can use LimitTo and Begin with dynamic variables that change when you hit ScrollTop and when you scroll to the bottom. But now I have a problem where the search only operates on what is in the list (restricted by the LimitTo) and not on the complete array. Is there a way to get it to search the underlying array instead? Any advice appreciated, I'm very close to having a solution to this problem!

sshalayev commented 7 years ago

IMHO, root of the problem is in intensive and undjustify use of forced digesting. Also I'd say that ngModel is used rather strange way. I didn't find a single $commitViewValue in the code. Making long things short. After removing unnecessary calls to digest and doing small tweaks on how ngModel and $compile are used, I've got it working with suitable performance on large arrays. It's not ready for PR perhaps - I've tested only functionality I need. Hopefully someone can get good insights out of it

user378230 commented 7 years ago

@sshalayev it's a real shame you couldn't contribute your changes back to us here, I'm sure a lot of people would have been grateful if you did.

Consider submitting them in smaller chunks if you want, it will save you having to constantly keep your own fork up to date after all! 😁

cristianocoelho commented 7 years ago

Adding comments here since I'm also interested in a way to speed up performance, 200 rows already is a killer.

gercheq commented 7 years ago

@cristianocoelho @user378230 Checkout this one for a working version: https://github.com/angular-ui/ui-select/issues/88#issuecomment-229049936

gscoppino commented 6 years ago

We've also been having some performance issues with ui-select, specifically with lists of over 100 items. Disabling AngularJS driven animations by removing the ngAnimate module significantly improves perceived performance as noted by @Anton-Rodionov earlier. However, we only want to disable animations for ui-select and want other parts of our app to continue using AngularJS driven animations. A solution was found here in this issue description: https://github.com/angular-ui/ui-select/issues/1502

In addition, there is a way to make the change on a per-instance basis and without forking the repository or keeping a locally modified copy of the library. We created a directive that requires uiSelect as a sibling and sets the instance of $animate to null, like so:

angular.module('my-module')
    .directive('uiSelectNoAnimate', () => ({
        restrict: 'A',
        require: 'uiSelect',
        link: (scope, element, attrs, $select) => {
            $select.$animate = null;
        }
    }));

and use as follows:

<ui-select ui-select-no-animate ...>...</ui-select>