6

I'm using md-virtual-repeat directive of Angular Material to have an infinite scroll, and I need to replace it's demo $timeout function with a $http request. But I can't get to the right solution. In the code below, infinite scroll works fine but doesn't show the data from http request. The problem is that I don't know the way of binding $http result to infiniteItems.

Here is the plunker.

Index.html

<body ng-app="infiniteScrolling" class="virtualRepeatdemoInfiniteScroll">
<div ng-controller="AppCtrl as ctrl" ng-cloak>
    <md-content layout="column">
        <md-virtual-repeat-container id="vertical-container" flex>
            <div md-virtual-repeat="item in ctrl.infiniteItems" md-on-demand
                 class="repeated-item" flex>
                {{item.id}}
            </div>
        </md-virtual-repeat-container>
    </md-content>
</div>
</body>

JS:

(function () {
'use strict';
angular
  .module('infiniteScrolling', ['ngMaterial'])
  .controller('AppCtrl', function ($timeout,$scope,$http) {
     this.infiniteItems = {
          numLoaded_: 0,
          toLoad_: 0,
          items:[],
          getItemAtIndex: function (index) {
              if (index > this.numLoaded_) {
                  this.fetchMoreItems_(index);
                  return null;
              }
              return index;
          },
          getLength: function () {
              return this.numLoaded_ + 5;
          },
          fetchMoreItems_: function (index) {
               if (this.toLoad_ < index) {
                  this.toLoad_ += 20;

                  $http.get('items.json').success(function (data) {
                      var items = data;
                      for (var i = 0; i < items.length; i++) {
                          this.items.push(items[i].data);
                      }
                      this.numLoaded_ = this.toLoad_;
                  }.bind(this));
              }
          }
      };
   });
})();

3 Answers 3

11

This one works:

plnkr

  • getItemAtIndex returned the index and not the item
  • if you inspected what you pushed, you'd see that at line 33 in my plunkr I concat obj.data, not plain obj
(function () {
    'use strict';
    angular.module('infiniteScrolling', ['ngMaterial'])
      .controller('AppCtrl', function ($scope, $http) {
          // In this example, we set up our model using a plain object.
          // Using a class works too. All that matters is that we implement
          // getItemAtIndex and getLength.
          var vm = this;
          vm.infiniteItems = {
              numLoaded_: 0,
              toLoad_: 0,
              items: [],

              // Required.
              getItemAtIndex: function (index) {
                  if (index > this.numLoaded_) {
                      this.fetchMoreItems_(index);
                      return null;
                  }
                  return this.items[index];
              },

              // Required.
              getLength: function () {
                  return this.numLoaded_ + 5;
              },

              fetchMoreItems_: function (index) {
                  if (this.toLoad_ < index) {
                      this.toLoad_ += 5;
                      $http.get('items.json').then(angular.bind(this, function (obj) {
                          this.items = this.items.concat(obj.data);
                          this.numLoaded_ = this.toLoad_;
                      }));
                  }
              }
          }
      })
})();
Sign up to request clarification or add additional context in comments.

6 Comments

Thank you, It works. But I don't know why for toLoad_+=20 , when I go down the scroll the data loads with a big delay, very down never loads not the same for toLoad_+=10 .
Well, actually it creates element in the DOM but since your api returns 5 elements, iirc, it has not enough data to initialize, so they are blank/empty.
Thanks, you are right. I just wants to add one more point: I think replacing data with obj.data is not part of the solution as I was using success instead of then and it returns data not result, but using concat is part of the solution. Because when I remove that the code not work correctly. It has a downside as it store all data (all out of view data) in memory not only the data in view.
@A.M About the success vs then solution I presented: success and errorhave been deprecated
This actually works for me, except that I would want the list to stop scrolling once all the data is displayed. This solution repeats the json.data from the start again.
|
0

On every api call try to get wheather the db has few more records or not. and add that condition in fetchMoreItems_ function.

fetchMoreItems_: function (index) {
              if (this.toLoad_ < index && hasMoreRecords) {
                  this.toLoad_ += 5;

In our code we get the details like

  • sCurrentPage : 3
  • sMore: true ==>>this indicates that the db has more records or not after fetching page wise data.
  • sTotalPages: 4
  • sTotalRecords : 36

1 Comment

And your saying it creates element in the DOM and if we get lesser records that shows blank records. So how can I remove or stop generating those blank records. Can you please help...
0

came here and saw @alessandro-buggin answer which was very helpful. I had to change it a little bit, so I thought of sharing it for others to help. I needed:

  • to avoid getting scroll requests while already recovering data (using this.hold)
  • stopping requests when the whole data was received from the backend (using this.stop_)
  • hiding the content while loading, to avoid glitches or empty elements (again using this.hold). On the view you need to use ng-hide on that element because ng-if avoid the element to ever exist so it won't load the first time.
  • implementing a refresh method to reload data when parameters/filters changed from an outside form.

Far from perfect but works pretty well.

vm.elements = null;
vm.infiniteItems = { // Start of infinte logic

stop_: false,
hold: false,
numLoaded_: 0,
toLoad_: 0,
items: [],

refresh: function() {
    this.stop_ = false;
    this.hold = false;
    this.numLoaded_ = 0;
    this.toLoad_ = 0;
    this.items = [];
},

getItemAtIndex: function (index) {
    if (!this.hold) {
        if (index > this.numLoaded_) {
            this.fetchMoreItems_(index);
            return null;
        }
    }
    return this.items[index];
},

getLength: function () {
    if (this.stop_) {
        return this.items.length;
    }
    return this.numLoaded_ + 5;
},

fetchMoreItems_: function (index) {
    if (this.toLoad_ < index) {

        this.hold = true;
        this.toLoad_ += 5;

        var start = this.numLoaded_;
        if (start > 0) start++;

        MyService.getData(parameters)
         .then(angular.bind(this, function (obj) {

          if (obj && obj.elements > 0) {
            vm.elements = obj.elements;
            this.items = this.items.concat(obj.data);

            if (obj.elements < this.toLoad_) {
                this.stop_ = true;
            }
            this.numLoaded_ = this.items.length;
            this.hold = false;

          } else { // if no data
            vm.elements = 0;
          }
        }));
    }
}

} // End of infinte logic

Note: my service returns an object composed like this: obj = {elements: INTEGER, data: ARRAY} where elements tells you the length of the full query.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.