AngularJS how to do lazy load of images with filters
Asked Answered
J

2

6

I'm developing an angular application where the main page loads 1000 images, but the user can only look at 20 at a time. I will also have several filters on my list, so that it can be filtered and sorted based on different criteria.

I've tried http://binarymuse.github.io/ngInfiniteScroll/# and http://ngscroller.herokuapp.com/ but neither seems to work that well.

Ngscroller does work but it breaks when I try to apply my filters. I also prefer this one since it does not require me to include jquery. Are there any simple directives out there that can do what I need to? I'm trying to speed up my web page but I don't want to reinvent the wheel if there is something out there which already accomplishes this.

Here is my attempt with ngScroller: http://plnkr.co/edit/r0uhV3OxT2USxmrBQk22?p=preview

<div class="content" ng-controller="MainController" ng-scroller="content">
  <ul class="list" ng-scroller-repeat="item in items | filter:idOver500k | orderBy:predicate:!reverse">
    <li class="item">{{item.text}}</li>
  </ul>
</div>

The scroll works without the filter and orderBy, but I'm looking for a method that will handle all cases.

It takes at least 3 seconds longer to load my page than it does if I remove the images. It looks like angular is loading only when all of the images are obtained. What is the best way to handle this?

Thank you!

Jehovist answered 23/12, 2013 at 16:32 Comment(4)
Maybe you can control the filter to display only part of the array, and increase the number when scroll down. Reset the number when filter option changed.Chapter
you demo is broken: Cannot read property 'A' of undefinedVegetate
@artur Yes, I know the demo is broken. It only works without filters, which is the problem. I updated the plunk to default to a working (no filter) versionJehovist
@Chapter - are you suggesting I maintain two arrays? I suppose I could create items and itemsDisplay, and then add more from items into itemsDisplay when the scroll event triggers. That will work for page loads, but I'm not sure the best way to handle that when I need to filter all of the items, since the filter would then only apply to the smaller list. Thoughts?Jehovist
C
9

Demo with ng-infinite-scroll: http://plnkr.co/edit/O5w0Fp

ng-infinite-scroll has different mechanism with ng-scroller. infinite-scroll will trigger a loadMore function when user scroll to the bottom. You can define loadMore function yourself.

I created a lazyLoad filter to only only return part of the filtered items, managed by counter variable. When scroll to the bottom, counter will be incremented by one to return more items from lazyLoad filter.

When user change the order by parameter, counter will be reset to 1.

When counter equals 1, it will load the first 30 items.

Notes

It may have problem if the height of document.body is less than the height of window, because that way document will not be able to scroll thus will not trigger scroll event. You have to manually check the heights and trigger loadMoreItems accordingly.

The problem will occur while page initialize or counter reset.

I added adjustCounter function to run after reset the counter. ng-infinite-scroll will handle this when page load internally.

Chapter answered 30/12, 2013 at 20:32 Comment(2)
Okay, this seems to work! Can you explain why you use rootScope for the counter instead of the controller scope? Is it just so you don't have to pass it in as an argument to the filter or is there some other reason I'm not seeing?Jehovist
@Marianna I just don't want to pass counter as an argument for filter. I think either way will work.Chapter
S
0

Looks like this isn't going to work with a dynamic filter without fixing ngScroller.

The offending code seems to be in the ng.Scroller.prototype.compile function:

var exp = carousel.getAttribute('ng-scroller-repeat');
var keys = exp.split(/\s+in\s+/);

Where ngScroller basically splits the attribute without looking at filters or the like.

One possibe workaround is like @Daiwei suggests and filter before binding. Not such a bad solution as this is a lot faster anyway.

  app.controller('MainController', function ($scope) {
    $scope.allItems = generateItems(1000);
    $scope.items = $scope.allItems.reduce(function(previous, item){
      if (item.id > 500000)
      {
        previous.push(item);
      }
      return previous;
    },[]); 
  });

See this Plunker for an example.

Strutting answered 30/12, 2013 at 16:3 Comment(2)
It seems like that kind of works assuming that you already know your sorting functions in advance. I can't seem to get it to work with the on-click functionality. Seems like ng-scroller isn't getting the update to items (and $scope.$apply() doesn't work) plnkr.co/edit/YL3fJXRNCntjypBdNJtN?p=previewJehovist
You can create a sorting function as a string on the fly and use Function() or eval() to turn it into a real function object. As far as updates goes it seems ngScroller is just maintaining a reference the original list and not using a $watch() to see when things have changed. It looks like @Chapter has a better solution using infiniteScroll. I have used this in the past and it worked well. I don't think loading jQuery is a big problem.Strutting

© 2022 - 2024 — McMap. All rights reserved.