Skip to content

Commit d58fff7

Browse files
committed
fix(ionInfiniteScroll): work properly if past horizontal boundaries
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.
1 parent 426a355 commit d58fff7

File tree

2 files changed

+97
-28
lines changed

2 files changed

+97
-28
lines changed

Diff for: js/ext/angular/src/directive/ionicContent.js

+28-8
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ function($timeout, $controller, $ionicBind) {
305305
* ```
306306
*/
307307
.directive('ionInfiniteScroll', ['$timeout', function($timeout) {
308+
function calculateMaxValue(distance, maximum, isPercent) {
309+
return isPercent ?
310+
maximum * (1 - parseInt(distance,10) / 100) :
311+
maximum - parseInt(distance, 10);
312+
}
308313
return {
309314
restrict: 'E',
310315
require: ['^$ionicScroll', 'ionInfiniteScroll'],
@@ -319,10 +324,17 @@ function($timeout, $controller, $ionicBind) {
319324
this.isLoading = false;
320325
this.scrollView = null; //given by link function
321326
this.getMaxScroll = function() {
322-
var dist = $attrs.distance || '1%';
323-
return dist.indexOf('%') > -1 ?
324-
this.scrollView.getScrollMax().top * (1 - parseInt(dist,10) / 100) :
325-
this.scrollView.getScrollMax().top - parseInt(dist, 10);
327+
var distance = ($attrs.distance || '1%').trim();
328+
var isPercent = distance.indexOf('%') !== -1;
329+
var maxValues = this.scrollView.getScrollMax();
330+
return {
331+
left: this.scrollView.options.scrollingX ?
332+
calculateMaxValue(distance, maxValues.left, isPercent) :
333+
-1,
334+
top: this.scrollView.options.scrollingY ?
335+
calculateMaxValue(distance, maxValues.top, isPercent) :
336+
-1
337+
};
326338
};
327339
}],
328340
link: function($scope, $element, $attrs, ctrls) {
@@ -342,14 +354,22 @@ function($timeout, $controller, $ionicBind) {
342354
infiniteScrollCtrl.isLoading = false;
343355
});
344356

345-
scrollCtrl.$element.on('scroll', ionic.animationFrameThrottle(function() {
346-
if (!infiniteScrollCtrl.isLoading &&
347-
scrollView.getValues().top >= infiniteScrollCtrl.getMaxScroll()) {
357+
scrollCtrl.$element.on('scroll', ionic.animationFrameThrottle(checkInfiniteBounds));
358+
setTimeout(checkInfiniteBounds);
359+
360+
function checkInfiniteBounds() {
361+
if (infiniteScrollCtrl.isLoading) return;
362+
363+
var scrollValues = scrollView.getValues();
364+
var maxScroll = infiniteScrollCtrl.getMaxScroll();
365+
366+
if ((maxScroll.left !== -1 && scrollValues.left >= maxScroll.left) ||
367+
(maxScroll.top !== -1 && scrollValues.top >= maxScroll.top)) {
348368
$element[0].classList.add('active');
349369
infiniteScrollCtrl.isLoading = true;
350370
$scope.$parent.$apply($attrs.onInfinite || '');
351371
}
352-
}));
372+
}
353373
}
354374
};
355375
}]);

Diff for: js/ext/angular/test/directive/ionicInfiniteScroll.unit.js

+69-20
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,38 @@ describe('ionicInfiniteScroll directive', function() {
22
beforeEach(module('ionic'));
33

44
var scrollTopValue;
5-
var scrollMaxValue;
5+
var scrollTopMaxValue;
6+
var scrollLeftValue;
7+
var scrollLeftMaxValue;
68
var ctrl;
7-
function setup(attrs, scopeProps) {
9+
function setup(attrs, scopeProps, options) {
810
var element;
911
scrollTopValue = 50;
10-
scrollMaxValue = 101;
12+
scrollLeftValue = 60;
13+
scrollLeftMaxValue = 101;
14+
scrollTopMaxValue = 121;
1115
inject(function($rootScope, $compile) {
1216
var scope = $rootScope.$new();
1317
angular.extend(scope, scopeProps || {});
1418
element = angular.element('<ion-infinite-scroll '+(attrs||'')+'></ion-infinite-scroll>');
1519
ionic.animationFrameThrottle = function(cb) { return function() { cb(); }; };
1620
element.data('$$ionicScrollController', {
1721
scrollView: {
22+
options: angular.extend({
23+
scrollingX: true,
24+
scrollingY: true
25+
}, options || {}),
1826
getValues: jasmine.createSpy('getValues').andCallFake(function() {
19-
return { top: scrollTopValue };
27+
return {
28+
left: scrollLeftValue,
29+
top: scrollTopValue
30+
};
2031
}),
2132
getScrollMax: jasmine.createSpy('getScrollMax').andCallFake(function() {
22-
return { top: scrollMaxValue };
33+
return {
34+
left: scrollLeftMaxValue,
35+
top: scrollTopMaxValue
36+
};
2337
}),
2438
resize: jasmine.createSpy('resize')
2539
},
@@ -70,27 +84,62 @@ describe('ionicInfiniteScroll directive', function() {
7084
});
7185

7286
describe('getMaxScroll', function() {
73-
it('getMaxScroll should default to 1%', function() {
74-
var el = setup();
75-
expect(ctrl.getMaxScroll()).toBe(101 * 0.99);
76-
});
77-
78-
it('getMaxScroll should use attr.distance as number', function() {
79-
var el = setup('distance=3');
80-
expect(ctrl.getMaxScroll()).toBe(98);
81-
});
87+
[ { scrollingX: true, scrollingY: true, },
88+
{ scrollingX: false, scrollingY: true },
89+
{ scrollingX: true, scrollingY: false }
90+
].forEach(function(opts) {
91+
92+
describe('with scrollingX='+opts.scrollingX+', scrollingY='+opts.scrollingY, function() {
93+
it('should default to 1%', function() {
94+
var el = setup('', {}, opts);
95+
expect(ctrl.getMaxScroll()).toEqual({
96+
left: opts.scrollingX ? scrollLeftMaxValue * 0.99 : -1,
97+
top: opts.scrollingY ? scrollTopMaxValue * 0.99 : -1
98+
});
99+
});
100+
101+
it('should use attr.distance as number', function() {
102+
var el = setup('distance=3', {}, opts);
103+
expect(ctrl.getMaxScroll()).toEqual({
104+
left: opts.scrollingX ? scrollLeftMaxValue - 3 : -1,
105+
top: opts.scrollingY ? scrollTopMaxValue - 3 : -1
106+
});
107+
});
108+
109+
it('should use attr.distance as percent', function() {
110+
var el = setup('distance=5%', {}, opts);
111+
expect(ctrl.getMaxScroll()).toEqual({
112+
left: opts.scrollingX ? scrollLeftMaxValue * 0.95 : -1,
113+
top: opts.scrollingY ? scrollTopMaxValue * 0.95 : -1
114+
});
115+
});
116+
});
82117

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

89121
describe('scroll event', function() {
90122

91-
it('should add active and call attr.onInfinite if past top', function() {
123+
it('should do nothing if < left and top', function() {
124+
var el = setup('on-infinite="foo=1"');
125+
el.controller('$ionicScroll').$element.triggerHandler('scroll');
126+
127+
expect(el.hasClass('active')).toBe(false);
128+
expect(ctrl.isLoading).toBe(false);
129+
expect(el.scope().foo).not.toBe(1);
130+
});
131+
it('should add active and call attr.onInfinite if >= top', function() {
132+
var el = setup('on-infinite="foo=1"');
133+
scrollTopValue = scrollTopMaxValue;
134+
el.controller('$ionicScroll').$element.triggerHandler('scroll');
135+
136+
expect(el.hasClass('active')).toBe(true);
137+
expect(ctrl.isLoading).toBe(true);
138+
expect(el.scope().foo).toBe(1);
139+
});
140+
it('should add active and call attr.onInfinite if >= left', function() {
92141
var el = setup('on-infinite="foo=1"');
93-
scrollTopValue = scrollMaxValue;
142+
scrollLeftValue = scrollLeftMaxValue;
94143
el.controller('$ionicScroll').$element.triggerHandler('scroll');
95144

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

106155
expect(el.hasClass('active')).toBe(true);

0 commit comments

Comments
 (0)