Skip to content

Commit

Permalink
fix(ionInfiniteScroll): work properly if past horizontal boundaries
Browse files Browse the repository at this point in the history
Fixes #1073

ion-infinite-scroll will now fire a scroll event if the user scrolls
past the left boundaries (if they exist) or the top boundaries (if they
exists).  This means infinite scroll works for vertical, horizontal, or
vertical plus horizontal scrolling situations.
  • Loading branch information
ajoslin committed Apr 9, 2014
1 parent 426a355 commit d58fff7
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 28 deletions.
36 changes: 28 additions & 8 deletions js/ext/angular/src/directive/ionicContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ function($timeout, $controller, $ionicBind) {
* ```
*/
.directive('ionInfiniteScroll', ['$timeout', function($timeout) {
function calculateMaxValue(distance, maximum, isPercent) {
return isPercent ?
maximum * (1 - parseInt(distance,10) / 100) :
maximum - parseInt(distance, 10);
}
return {
restrict: 'E',
require: ['^$ionicScroll', 'ionInfiniteScroll'],
Expand All @@ -319,10 +324,17 @@ function($timeout, $controller, $ionicBind) {
this.isLoading = false;
this.scrollView = null; //given by link function
this.getMaxScroll = function() {
var dist = $attrs.distance || '1%';
return dist.indexOf('%') > -1 ?
this.scrollView.getScrollMax().top * (1 - parseInt(dist,10) / 100) :
this.scrollView.getScrollMax().top - parseInt(dist, 10);
var distance = ($attrs.distance || '1%').trim();
var isPercent = distance.indexOf('%') !== -1;
var maxValues = this.scrollView.getScrollMax();
return {
left: this.scrollView.options.scrollingX ?
calculateMaxValue(distance, maxValues.left, isPercent) :
-1,
top: this.scrollView.options.scrollingY ?
calculateMaxValue(distance, maxValues.top, isPercent) :
-1
};
};
}],
link: function($scope, $element, $attrs, ctrls) {
Expand All @@ -342,14 +354,22 @@ function($timeout, $controller, $ionicBind) {
infiniteScrollCtrl.isLoading = false;
});

scrollCtrl.$element.on('scroll', ionic.animationFrameThrottle(function() {
if (!infiniteScrollCtrl.isLoading &&
scrollView.getValues().top >= infiniteScrollCtrl.getMaxScroll()) {
scrollCtrl.$element.on('scroll', ionic.animationFrameThrottle(checkInfiniteBounds));
setTimeout(checkInfiniteBounds);

function checkInfiniteBounds() {
if (infiniteScrollCtrl.isLoading) return;

var scrollValues = scrollView.getValues();
var maxScroll = infiniteScrollCtrl.getMaxScroll();

if ((maxScroll.left !== -1 && scrollValues.left >= maxScroll.left) ||
(maxScroll.top !== -1 && scrollValues.top >= maxScroll.top)) {
$element[0].classList.add('active');
infiniteScrollCtrl.isLoading = true;
$scope.$parent.$apply($attrs.onInfinite || '');
}
}));
}
}
};
}]);
Expand Down
89 changes: 69 additions & 20 deletions js/ext/angular/test/directive/ionicInfiniteScroll.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,38 @@ describe('ionicInfiniteScroll directive', function() {
beforeEach(module('ionic'));

var scrollTopValue;
var scrollMaxValue;
var scrollTopMaxValue;
var scrollLeftValue;
var scrollLeftMaxValue;
var ctrl;
function setup(attrs, scopeProps) {
function setup(attrs, scopeProps, options) {
var element;
scrollTopValue = 50;
scrollMaxValue = 101;
scrollLeftValue = 60;
scrollLeftMaxValue = 101;
scrollTopMaxValue = 121;
inject(function($rootScope, $compile) {
var scope = $rootScope.$new();
angular.extend(scope, scopeProps || {});
element = angular.element('<ion-infinite-scroll '+(attrs||'')+'></ion-infinite-scroll>');
ionic.animationFrameThrottle = function(cb) { return function() { cb(); }; };
element.data('$$ionicScrollController', {
scrollView: {
options: angular.extend({
scrollingX: true,
scrollingY: true
}, options || {}),
getValues: jasmine.createSpy('getValues').andCallFake(function() {
return { top: scrollTopValue };
return {
left: scrollLeftValue,
top: scrollTopValue
};
}),
getScrollMax: jasmine.createSpy('getScrollMax').andCallFake(function() {
return { top: scrollMaxValue };
return {
left: scrollLeftMaxValue,
top: scrollTopMaxValue
};
}),
resize: jasmine.createSpy('resize')
},
Expand Down Expand Up @@ -70,27 +84,62 @@ describe('ionicInfiniteScroll directive', function() {
});

describe('getMaxScroll', function() {
it('getMaxScroll should default to 1%', function() {
var el = setup();
expect(ctrl.getMaxScroll()).toBe(101 * 0.99);
});

it('getMaxScroll should use attr.distance as number', function() {
var el = setup('distance=3');
expect(ctrl.getMaxScroll()).toBe(98);
});
[ { scrollingX: true, scrollingY: true, },
{ scrollingX: false, scrollingY: true },
{ scrollingX: true, scrollingY: false }
].forEach(function(opts) {

describe('with scrollingX='+opts.scrollingX+', scrollingY='+opts.scrollingY, function() {
it('should default to 1%', function() {
var el = setup('', {}, opts);
expect(ctrl.getMaxScroll()).toEqual({
left: opts.scrollingX ? scrollLeftMaxValue * 0.99 : -1,
top: opts.scrollingY ? scrollTopMaxValue * 0.99 : -1
});
});

it('should use attr.distance as number', function() {
var el = setup('distance=3', {}, opts);
expect(ctrl.getMaxScroll()).toEqual({
left: opts.scrollingX ? scrollLeftMaxValue - 3 : -1,
top: opts.scrollingY ? scrollTopMaxValue - 3 : -1
});
});

it('should use attr.distance as percent', function() {
var el = setup('distance=5%', {}, opts);
expect(ctrl.getMaxScroll()).toEqual({
left: opts.scrollingX ? scrollLeftMaxValue * 0.95 : -1,
top: opts.scrollingY ? scrollTopMaxValue * 0.95 : -1
});
});
});

it('getMaxScroll should use attr.distance as percent', function() {
var el = setup('distance=5%');
expect(ctrl.getMaxScroll()).toBe(101 * 0.95);
});
});

describe('scroll event', function() {

it('should add active and call attr.onInfinite if past top', function() {
it('should do nothing if < left and top', function() {
var el = setup('on-infinite="foo=1"');
el.controller('$ionicScroll').$element.triggerHandler('scroll');

expect(el.hasClass('active')).toBe(false);
expect(ctrl.isLoading).toBe(false);
expect(el.scope().foo).not.toBe(1);
});
it('should add active and call attr.onInfinite if >= top', function() {
var el = setup('on-infinite="foo=1"');
scrollTopValue = scrollTopMaxValue;
el.controller('$ionicScroll').$element.triggerHandler('scroll');

expect(el.hasClass('active')).toBe(true);
expect(ctrl.isLoading).toBe(true);
expect(el.scope().foo).toBe(1);
});
it('should add active and call attr.onInfinite if >= left', function() {
var el = setup('on-infinite="foo=1"');
scrollTopValue = scrollMaxValue;
scrollLeftValue = scrollLeftMaxValue;
el.controller('$ionicScroll').$element.triggerHandler('scroll');

expect(el.hasClass('active')).toBe(true);
Expand All @@ -100,7 +149,7 @@ describe('ionicInfiniteScroll directive', function() {
it('should not run the event twice if isLoading is true', function() {
var onScrollSpy = jasmine.createSpy('onInfiniteScroll');
var el = setup('', { $onInfiniteScroll: onScrollSpy });
scrollTopValue = scrollMaxValue;
scrollTopValue = scrollTopMaxValue;
el.controller('$ionicScroll').$element.triggerHandler('scroll');

expect(el.hasClass('active')).toBe(true);
Expand Down

0 comments on commit d58fff7

Please sign in to comment.