implementing a lazy load and infinite scroll in AngularJS

I am planning to refactor this into a more simple, modular directive, but before doing so, I just want to describe how I approached implementing a lazy load and infinite scroll on a project I picked up. It's a dashboard for tracking individual and group progress. (Sidenote: I also would like to modularize it into an easy, near-instant dashboard library. The version I'm currently building interfaces with 2 external APIs (Github and, as well as an API that I implemented in a nodeJS server.)

This implemenation is dependent on an API that responds once with the entire data set that will need to be fetched. Originally, I designed the api to respond to multiple calls from the client because I thought that returning too much would negetivley impact browser performance. But, that was naive. The browser can handle this without breaking a sweat.

// listController.js //

angular.module('myApp').controller('listController', ['$scope','dataService','$timeout',  function($scope, dataService, $timeout){

  $ = {};
  $scope.spinner = {};
  $ = [];

  // used to show and hide the table element that will display the data
  $scope.isLoading = true;

  // used to manipulate the element containing the spinner once the data table
  // is displayed
  $scope.spinner.hide = false;

  // this will store the bulk of the data from the initial response.
  // then we will splice items off as needed
  var cache = { data: { mongo: [] } };

  var getData = function(){

    // check that the view-bound data set is empty
    if ($ < 1){


          // display the table element
          $scope.isLoading = false;

          // declare the $timeout with a reference in order to cancel
          // it later. the $timeout invokes the AngularJS digest cycle
          // and will update the view bound to the data
          var appendDataTimer = $timeout(function(){

            // splice some data to append to the view
            $ = $, 12));

            // assign some other data hashes from the response to the $scope
            // so they can be referenced in the view
            $ =;
            $ =;

            // remove the spinner from the view
            $scope.spinner.hide = true;


          // invoke the timeout and remove it when the promise resolves

    } else {
      // the view contained data, so continue to splice from the cache and add // to the view data
      $ = $, 12));

      // delays feel more 'magical'
        $scope.spinner.hide = true;

  // to be invoked on the scroll event in our directive, which will be
  // triggered once we scroll through some data that is already displayed
  $scope.loadMore = function(){
    $scope.spinner.hide = false;
    var loadTimer = $timeout(function(){
    }, 2000)

  // invoke the retrieval of data
// _list_partial.html (without styles) //

<table class="table table-striped table-hover" ng-if="!isLoading" when-scrolled="loadMore()">  
      <tr ng-repeat="datum in data.mongo">
          <span>{{ datum.First }} {{ datum.Last }}</span><br>
        <td class="cohort-stats-col cohort-score"> some stats </td>
        <td ><textarea rows="3">{{ datum.comments }}</textarea></td>
  <div ng-hide="spinner.hide">
    <img src="img/spinner.gif">
// lazyLoadDirective.js //

.directive('whenScrolled', function($window, $timeout) {
  return {
    restrict: "A",
    link: function(scope, element, attr) {
      var top = angular.element($window)[0].screenTop;
      var origHeight = angular.element($window)[0].screen.height;
      var height = (origHeight * 0.9);

      // bind the digest cycle to be triggered by the scroll event
      // when it exceeds a threshold
      angular.element($window).bind('scroll', function() {
        if (angular.element($window)[0].scrollY >= (height)) {

          // show the spinner when triggered
          scope.spinner.hide = !scope.spinner.hide;

            // invoke the function passed into the 'whenScrolled' attribute

            // increment the threshold
            height += (origHeight * 1.5);