Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ideal way of doing infinite scroll and real time updates in Meteor

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?

like image 573
HGandhi Avatar asked Jan 05 '13 01:01

HGandhi


2 Answers

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?

like image 51
Andrej Avatar answered Oct 05 '22 22:10

Andrej


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

like image 41
Bojan Djurdjevic Avatar answered Oct 05 '22 22:10

Bojan Djurdjevic