@@ -311,6 +311,15 @@ VirtualRepeatContainerController.prototype.handleScroll_ = function() {
311
311
* @param {string= } md-extra-name Evaluates to an additional name to which
312
312
* the current iterated item can be assigned on the repeated scope. (Needed
313
313
* for use in md-autocomplete).
314
+ * @param {boolean= } md-on-demand When present, treats the md-virtual-repeat argument
315
+ * as an object that can fetch rows rather than an array.
316
+ * NOTE: This object must implement the following interface with two (2) methods:
317
+ * getItemAtIndex: function(index) -> item at that index or null if it is not yet
318
+ * loaded (It should start downloading the item in that case).
319
+ * getLength: function() -> number The data legnth to which the repeater container
320
+ * should be sized. Ideally, when the count is known, this method should return it.
321
+ * Otherwise, return a higher number than the currently loaded items to produce an
322
+ * infinite-scroll behavior.
314
323
*/
315
324
function VirtualRepeatDirective ( $parse ) {
316
325
return {
@@ -344,12 +353,16 @@ function VirtualRepeatController($scope, $element, $attrs, $browser, $document,
344
353
this . $document = $document ;
345
354
this . $$rAF = $$rAF ;
346
355
356
+ /** @type {boolean } Whether we are in on-demand mode. */
357
+ this . onDemand = $attrs . hasOwnProperty ( 'mdOnDemand' ) ;
347
358
/** @type {!Function } Backup reference to $browser.$$checkUrlChange */
348
359
this . browserCheckUrlChange = $browser . $$checkUrlChange ;
349
360
/** @type {number } Most recent starting repeat index (based on scroll offset) */
350
361
this . newStartIndex = 0 ;
351
362
/** @type {number } Most recent ending repeat index (based on scroll offset) */
352
363
this . newEndIndex = 0 ;
364
+ /** @type {number } Most recent end visible index (based on scroll offset) */
365
+ this . newVisibleEnd = 0 ;
353
366
/** @type {number } Previous starting repeat index (based on scroll offset) */
354
367
this . startIndex = 0 ;
355
368
/** @type {number } Previous ending repeat index (based on scroll offset) */
@@ -403,10 +416,12 @@ VirtualRepeatController.prototype.link_ =
403
416
this . container = container ;
404
417
this . transclude = transclude ;
405
418
this . repeatName = repeatName ;
406
- this . repeatListExpression = repeatListExpression ;
419
+ this . rawRepeatListExpression = repeatListExpression ;
407
420
this . extraName = extraName ;
408
421
this . sized = false ;
409
422
423
+ this . repeatListExpression = angular . bind ( this , this . repeatListExpression_ ) ;
424
+
410
425
this . container . register ( this ) ;
411
426
} ;
412
427
@@ -434,6 +449,25 @@ VirtualRepeatController.prototype.readItemSize_ = function() {
434
449
} ;
435
450
436
451
452
+ /**
453
+ * Returns the user-specified repeat list, transforming it into an array-like
454
+ * object in the case of infinite scroll/dynamic load mode.
455
+ * @param {!angular.Scope } The scope.
456
+ * @return {!Array|!Object } An array or array-like object for iteration.
457
+ */
458
+ VirtualRepeatController . prototype . repeatListExpression_ = function ( scope ) {
459
+ var repeatList = this . rawRepeatListExpression ( scope ) ;
460
+
461
+ if ( this . onDemand && repeatList ) {
462
+ var virtualList = new VirtualRepeatModelArrayLike ( repeatList ) ;
463
+ virtualList . $$includeIndexes ( this . newStartIndex , this . newVisibleEnd ) ;
464
+ return virtualList ;
465
+ } else {
466
+ return repeatList ;
467
+ }
468
+ } ;
469
+
470
+
437
471
/**
438
472
* Called by the container. Informs us that the containers scroll or size has
439
473
* changed.
@@ -467,6 +501,9 @@ VirtualRepeatController.prototype.containerUpdated = function() {
467
501
if ( this . newStartIndex !== this . startIndex ||
468
502
this . newEndIndex !== this . endIndex ||
469
503
this . container . getScrollOffset ( ) > this . container . getScrollSize ( ) ) {
504
+ if ( this . items instanceof VirtualRepeatModelArrayLike ) {
505
+ this . items . $$includeIndexes ( this . newStartIndex , this . newEndIndex ) ;
506
+ }
470
507
this . virtualRepeatUpdate_ ( this . items , this . items ) ;
471
508
}
472
509
} ;
@@ -503,7 +540,7 @@ VirtualRepeatController.prototype.virtualRepeatUpdate_ = function(items, oldItem
503
540
}
504
541
505
542
this . items = items ;
506
- if ( items !== oldItems ) {
543
+ if ( items !== oldItems || lengthChanged ) {
507
544
this . updateIndexes_ ( ) ;
508
545
}
509
546
@@ -684,6 +721,55 @@ VirtualRepeatController.prototype.updateIndexes_ = function() {
684
721
this . newStartIndex = Math . max ( 0 , Math . min (
685
722
itemsLength - containerLength ,
686
723
Math . floor ( this . container . getScrollOffset ( ) / this . itemSize ) ) ) ;
687
- this . newEndIndex = Math . min ( itemsLength , this . newStartIndex + containerLength + NUM_EXTRA ) ;
724
+ this . newVisibleEnd = this . newStartIndex + containerLength + NUM_EXTRA ;
725
+ this . newEndIndex = Math . min ( itemsLength , this . newVisibleEnd ) ;
688
726
this . newStartIndex = Math . max ( 0 , this . newStartIndex - NUM_EXTRA ) ;
689
727
} ;
728
+
729
+ /**
730
+ * This VirtualRepeatModelArrayLike class enforces the interface requirements
731
+ * for infinite scrolling within a mdVirtualRepeatContainer. An object with this
732
+ * interface must implement the following interface with two (2) methods:
733
+ *
734
+ * getItemAtIndex: function(index) -> item at that index or null if it is not yet
735
+ * loaded (It should start downloading the item in that case).
736
+ *
737
+ * getLength: function() -> number The data legnth to which the repeater container
738
+ * should be sized. Ideally, when the count is known, this method should return it.
739
+ * Otherwise, return a higher number than the currently loaded items to produce an
740
+ * infinite-scroll behavior.
741
+ *
742
+ * @usage
743
+ * <hljs lang="html">
744
+ * <md-virtual-repeat-container md-orient-horizontal>
745
+ * <div md-virtual-repeat="i in items" md-on-demand>
746
+ * Hello {{i}}!
747
+ * </div>
748
+ * </md-virtual-repeat-container>
749
+ * </hljs>
750
+ *
751
+ */
752
+ function VirtualRepeatModelArrayLike ( model ) {
753
+ if ( ! angular . isFunction ( model . getItemAtIndex ) ||
754
+ ! angular . isFunction ( model . getLength ) ) {
755
+ throw Error ( 'When md-on-demand is enabled, the Object passed to md-virtual-repeat must implement ' +
756
+ 'functions getItemAtIndex() and getLength() ' ) ;
757
+ }
758
+
759
+ this . model = model ;
760
+ }
761
+
762
+
763
+ VirtualRepeatModelArrayLike . prototype . $$includeIndexes = function ( start , end ) {
764
+ for ( var i = start ; i < end ; i ++ ) {
765
+ if ( ! this . hasOwnProperty ( i ) ) {
766
+ this [ i ] = this . model . getItemAtIndex ( i ) ;
767
+ }
768
+ }
769
+ this . length = this . model . getLength ( ) ;
770
+ } ;
771
+
772
+
773
+ function abstractMethod ( ) {
774
+ throw Error ( 'Non-overridden abstract method called.' ) ;
775
+ }
0 commit comments