I have a large set of data that I would like to be split into sections loaded through infinite scroll in Meteor in order to prevent having to load a large dataset all at once; instead I would load chunks of the dataset when needed. How do I do so while preserving live page updates on each section loaded by infinite scroll?
Here is the code I am using for a movie database playground. I experimented with jQuery events, but finally decided that it is enough if the data is transferred continuously. My testing collection is 680 records, with 20 fields and one thumbnail field which is between 10-20kb per cover. Here the Coffeescript code:
Movies = new Meteor.Collection("movies")
if Meteor.isClient
# starting with 10 items that the user sees a page fast
Session.set("queryLimit", 10)
# getting the item count from the server collection
Meteor.call 'getMoviesCount', (err, result) =>
Session.set('itemsInQuery',result)
# subscribe to a subset of the collection, and change the Session var on completion
# automatically changing the limit of the publishing function with autorun
Meteor.autorun ->
Meteor.subscribe "MoviesList", Session.get("queryLimit"), onComplete = ->
if Session.get("queryLimit") < Session.get('itemsInQuery')
queryLimit = Session.get("queryLimit") + 30 #change iterator depending on result size
Session.set("queryLimit", queryLimit )
# Client helper to add more items to handlebars template
Template.app.helpers movies: ->
Movies.find({})
if Meteor.isServer
Meteor.publish "MoviesList", (limit) ->
# show whole collections once limit is reached, by setting limit to 0
limit = 0 if limit > Movies.find().count()
console.log new Date(), limit
Movies.find({}, { limit: limit, fields: {title:1 } } ) #use fields to test different result sizes
Meteor.methods
getMoviesCount: (query) ->
return Movies.find().count()
And the html:
<body>
{{> app}}
</body>
<template name="app">
{{#each movies}}
<p>{{title}}</p>
{{/each}}
</template>
I did some quick performance test and it turns out that for a few lines of text per record, the quickest way to send data to the client is a limit around 100. I tried it also with 10-20kb thumbnails which are embedded in the file. When using bigger assets Chrome became quite unresponsive when the limit was bigger than 30 records. Here a few stats done on localhost (run 3x each):
limit seconds till last record rendered.
0 79s
010 121s
020 64s
030 45s
050 34s
100 16s
200 16s
It's interesting to note that it took around 79 s when meteor server sends the whole page in one go (limit=0). I am not sure, how this can be possible, because a continious stream should be the fastest. So there is probably something funny with my stats. Any ideas?
My solution is similar to Andrej's. Because I got a lot of records in database, I don't want my server to send them all at once, so on server I got:
Meteor.publish("items", function(page) {
if(!page)
page = 1;
var limit = 30 * page;
return Items.find({}, {limit: limit};
}
Client:
Template.templateName.items = function () {
Meteor.subscribe("items", Session.get("page"));
return Items.find({});
}
And a jQuery function to observe if page got to the bottom:
$(window).scroll(function(){
if ($(window).scrollTop() == $(document).height()-$(window).height()){
Session.set("page", Session.get("page") + 1);
}
});
Also to set page initial page on template created callback:
Template.templateName.created = function() {
Session.setDefault("page", 1);
};
And in template I'm showing these items using {{#each}} Also, I have to check if there are no more records
Better solution would be to show 30 when template is created and after that to get 30 more on scroll, but I don't know how to to show them. I may have a solution, but I'm lazy to implement because I'm not sure if it's going work. I'm thinking about appending rendered html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With