Infinite Scroll in Meteor/Angular

I know what I’m about to ask is a lot, but I’m working through my first app project with Meteor/Angular and I could use some pointers. I followed along with the “socially” tutorial on angular-meteor.com. Now I’m adapting the project for my own purposes. Rather than using the pagination that’s in the original project I’d like to implement infinite scrolling, only because I never have before. Found what appears to be a pretty good tutorial on that here: http://bluepiit.com/blog/2014/04/01/angularjs-infinite-scroll-pagination/.

I’m new to both Angular and Meteor, but I’m patient, persistent and enthusiastic to try this all out. But of course I have no experience with Angular to help me get through the real challenges. The challenge I’m facing (from what I’ve read in the Angular/Meteor tutorial) is that tutorial code it’s written in a “componentized” style, and the tutorial for the infinite scroll that I’m following is not. So I’m stuck trying to translate between the two. And with no experience, well, you can imagine.

Anyway, here goes, if you’re still following along. Here’s the important part of the code from the infinite scroll tutorial.

 scope.loadMoreCourses = function() {
 	if (scope.loadingResult) {
 		return;
 	}
 	if (scope.pagination.currentPage >= scope.pagination.noOfPages) {
 		return;
 	}
 	scope.pagination.currentPage = scope.pagination.currentPage + 1;
 	scope.offset = (scope.pagination.currentPage - 1) * scope.pagination.pageSize;
 	scope.limit = scope.pagination.pageSize;
 	scope.loadingResult = true;
 	scope.resultList = CourseService.courses.list({
 		offset: scope.offset,
 		limit: scope.limit
 	});
 	scope.loadingResult = false;
 };

 scope.initializeResultList = function() {
 	CourseService.courses.count({}, function(count) {
 		scope.total = count;
 		scope.pagination.noOfPages = Math.ceil(count / scope.pagination.pageSize);
 		scope.loadMoreCourses();
 	});
 }
 
 scope.initializeResultList();
 
 }]);

And here’s how I’ve translated it so far in my attempt to “componentize” it:


            this.loadMoreBuildings = function () {
              if (this.loadingResult) {
                return;
              }
              if (this.pagination.currentPage >= this.pagination.noOfPages) {
                return;
              }
              this.pagination.currentPage = this.pagination.currentPage + 1;
              this.offset = (this.pagination.currentPage - 1) * this.pagination.pageSize;
              this.limit = this.pagination.pageSize;
              this.loadingResult = true;
              this.resultList = this.buildings.list ({offset: this.offset, limit: this.limit});
              this.loadingResult = false;
            };

            this.initializeResultList = function () {
              this.buildings.count ({}, function (count) {
                this.total = count;
                this.pagination.noOfPages = Math.ceil (count / this.pagination.pageSize);
                this.loadMoreBuildings ();
              });
            };

            this.initializeResultList ();
        }
    }
});

When I run the app I get an error that says .count is not a method of undefined, when initializeResultList runs. OK, got it. It doesn’t know what buildings is. It is one of the helpers in the component. So how do I reference that helper in the initializeResultList in order to access it? Or is that the wrong approach?

Then, when I comment out things so LoadMoreBuildings actually runs, I’m getting an error that .list is not a method of undefined. Sure, it doesn’t know about it in one place so it doesn’t know about this one either. What is the correct way to reference the helper, assuming that’s what I’m supposed to be doing?

No bites? I must be asking a dumb question, or asking it in a dumb way. I’d be glad to try to clarify any questions you might have if you let me know where I’m going astray. Would it be best to ask this in an Angular forum, somewhere else?

It’s actually very similar to paging. You can use the counts package to help you

Set up publications as:

Meteor.publish('publicationName', function(page) {
  check(page,Number);
  return yourCollection.find({},{sort:{name:1},skip:page*10,limit:10});
});

Meteor.publish('publicationNameCount', function() {
  Counts.publish(this, 'nbItems', yourCollection.find(query,{fields:{_id:1}}));
});

Note that it’s important that you use a sort if you’re going to use limit and skip. (otherwise it can not use the oplog observer).

On the client side use a dynamic subscription and helpers like:

$reactive(this).attach($scope);
this.page = 0;
this.helpers({
 results: () => yourCollection.find({},{sort:{name:1}),
 nbResults: () => Counts.findOne('nbItems')
});
this.loadMoreBuildings = () => { this.page++ };
this.subscribe('publicationName', () => [this.getReactively('page')]);
this.subscribe('publicationNameCount');

Not tested, but something like that is all you need.

1 Like

I appreciate the response @seba. I’ll see if I can work this into my project and get it running. Thanks.

Be careful with the Counts package though. It is meant to be used on small collections. It made my app meltdown when I put it on a collection with > 100,000 docs in it. Instead, I just made a method that gets the count and made it autorun everytime the subscription changes. The drawback to this is that the count will not update when new docs are added/removed from the collection but there has been no subscription change. This wasn’t an issue for my purpose though.

Another approach would be to put a server-side autorun that tracks the Cursor.count() and publishes the result to a collection, but maybe this is what the Counts package is already doing, not sure.

1 Like

@danryan I like your idea of server side autorun to publish count. I’ll see if I can use that when my app needs it.