Paging data using AngularJS and RavenDB in the RAW Stack

So far loading and displaying some movies data has been pretty simple and straightforward. However something that was not immediately apparent was that not all movies in the database where shown. When opening the movies list there where just 128 movies visible and that was not all there was to show. In fact I have used the Rotten Tomatoes API to preload the database with more than 1000 movies and even though I never added a filter anywhere most of these didn’t show up. So what is going on?

 

RavenDB = Safe by default

RavenDB really is an awesome database because it actively tries to protect us against some serious mistakes. It default to a safe by default behavior which means it will not just return any number of documents from the database. In fact if you don’t include a number of documents to return it will automatically default to just return the first 128. Now you can just include a LINQ take() function to add more but even then it would stop at 1024 documents. Sure that second limit could be increased as well but what would the right number be? There is no right number as inserting more movies would just saturate the disk and network IO with the response.

The correct way to handle this is to introduce paging. And fortunately that is really easy to do in both the WebAPI and RavenDB. All we need to do is change the GetMovies() method in the WebAPI MoviesController. In this case I made the page size a fixed value but it would be easy enough to turn that into a second parameter.

 

   1: public IEnumerable<Movie> GetMovies(int page)

   2: {

   3:     const int pageSize = 128;

   4:  

   5:     var movies = _session.Query<Movie>()

   6:         .OrderBy(m => m.Title)

   7:         .Skip(page * pageSize)

   8:         .Take(pageSize)

   9:         .ToList();

  10:  

  11:     return movies;

  12: }

 

Updating the AngularJS client

With the server parts in place it is time to update the AngularJS client to load all movies. We could just loop and load until there is no more data to load but that would kind of defeat the purpose of paging. We can also add a button to get the user to load the next set of data. That would be simple enough, just keep track of the page number and load the next and/or previous page as needed. However as user I never really liked doing that, I very much prefer the browser to do that for me as I scroll through the list.

 

ngInfiniteScroll to the rescue

Using ngInfiniteScroll makes this really simple, just add a reference and point it to a function on the scope that loads each page of data. Now all you need to do is scroll and when you get close to the bottom of the list the next page is automatically loaded. And it works just as well on my tablet and phone as on my desktop PC. Sweet 🙂

 

A pretty small change to the AngularJS controller:

   1: var page = 0;

   2: $scope.movies = [];

   3: $scope.loadingData = false;

   4:  

   5: $scope.nextPage = function () {

   6:     $scope.loadingData = true;

   7:     $http.get("/api/movies?page=" + page).then(function (e) {

   8:         page++;

   9:         [].push.apply($scope.movies, e.data);

  10:         $scope.loadingData = false;

  11:     });

  12: };

 

And a small change to the view:

   1: <ul infinite-scroll='nextPage()' infinite-scroll-disabled='loadingData'>

   2:     <li ng-repeat="movie in movies">{{movie.Title}}</li>

   3: </ul>

 

Nice but not perfect

I really like this approach but there is still a potential problem. If there are a lot more movies in the database and the user just hold down the page down key the list in the browser keep on growing an d growing. And sooner or later that is going to slow down even the fastest browser.

 

Try it

The running application can be found here and the source on GitHub here.

 

Conclusion

The safe by default feature of RavenDB is really awesome and will protect a developer from accidentally returning much more data that is wise from a query. This is not the only part of the safe by default feature, there is more goodness here. I wish every database stack did the same out of the box.

 

Index of posts on the RAW stack

See here for more posts on the RAW stack.

Leave a Reply

Your email address will not be published. Required fields are marked *