Skip to content

Commit 9a3b965

Browse files
committed
feat(all): support infinite scroll
Provide a means to allow the user to continuously insert more data into the array being viewed. resolve: #5
1 parent 25fd4e1 commit 9a3b965

31 files changed

+727
-72
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,28 @@ With a surrounding fixed height container with overflow scroll. Note that `overf
8686

8787
If you are running the plugin in the `skeleton-naviagion` project, make sure to remove `overflow-x: hidden;` and `overflow-y: auto;` from `.page-host` in `styles.css`.
8888

89+
#### infinite scroll
90+
```html
91+
<template>
92+
<div virtual-repeat.for="item of items" virtual-repeat-next="getMore">
93+
${$index} ${item}
94+
</div>
95+
</template>
96+
```
97+
98+
```javascript
99+
export class MyVirtualList {
100+
items = ['Foo', 'Bar', 'Baz'];
101+
getMore() {
102+
for(let i = 0; i < 100; ++i) {
103+
this.items.push('item' + i);
104+
}
105+
}
106+
}
107+
```
108+
The `virtual-repeat-next` attribute can accept a function, a promise, or a function that returns a promise.
109+
The bound function will be called when the scroll container has reached a point where there are no more items to move into the DOM (i.e. when it reaches the end of a list).
110+
89111
## [Demo](http://aurelia.io/ui-virtualization/)
90112

91113
## Platform Support

build/tasks/test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ gulp.task('test', function (done) {
1111
}, done).start();
1212
});
1313

14+
/**
15+
* Run test and watch for changes
16+
*/
17+
gulp.task('test:watch', function (done) {
18+
new Karma({
19+
configFile: __dirname + '/../../karma.conf.js',
20+
singleRun: false
21+
}, done).start();
22+
});
23+
1424
/**
1525
* Watch for file changes and re-run tests on each change
1626
*/

config.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ System.config({
1414
},
1515

1616
map: {
17-
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
18-
"aurelia-bootstrapper": "npm:aurelia-bootstrapper@1.0.0-beta.2.0.1",
17+
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.2",
18+
"aurelia-bootstrapper": "npm:aurelia-bootstrapper@1.0.0-rc.1.0.1",
1919
"aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
20-
"aurelia-framework": "npm:aurelia-framework@1.0.0-rc.1.0.0",
20+
"aurelia-framework": "npm:aurelia-framework@1.0.0-rc.1.0.1",
2121
"aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
2222
"aurelia-logging-console": "npm:aurelia-logging-console@1.0.0-rc.1.0.0",
2323
"aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
@@ -56,15 +56,15 @@ System.config({
5656
"process": "github:jspm/nodelibs-process@0.1.2",
5757
"util": "npm:util@0.10.3"
5858
},
59-
"npm:aurelia-binding@1.0.0-rc.1.0.0": {
59+
"npm:aurelia-binding@1.0.0-rc.1.0.2": {
6060
"aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
6161
"aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
6262
"aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
6363
"aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-rc.1.0.0"
6464
},
65-
"npm:aurelia-bootstrapper@1.0.0-beta.2.0.1": {
65+
"npm:aurelia-bootstrapper@1.0.0-rc.1.0.1": {
6666
"aurelia-event-aggregator": "npm:aurelia-event-aggregator@1.0.0-rc.1.0.0",
67-
"aurelia-framework": "npm:aurelia-framework@1.0.0-rc.1.0.0",
67+
"aurelia-framework": "npm:aurelia-framework@1.0.0-rc.1.0.1",
6868
"aurelia-history": "npm:aurelia-history@1.0.0-rc.1.0.0",
6969
"aurelia-history-browser": "npm:aurelia-history-browser@1.0.0-rc.1.0.0",
7070
"aurelia-loader-default": "npm:aurelia-loader-default@1.0.0-rc.1.0.0",
@@ -85,8 +85,8 @@ System.config({
8585
"npm:aurelia-event-aggregator@1.0.0-rc.1.0.0": {
8686
"aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0"
8787
},
88-
"npm:aurelia-framework@1.0.0-rc.1.0.0": {
89-
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
88+
"npm:aurelia-framework@1.0.0-rc.1.0.1": {
89+
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.2",
9090
"aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
9191
"aurelia-loader": "npm:aurelia-loader@1.0.0-rc.1.0.0",
9292
"aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
@@ -136,12 +136,12 @@ System.config({
136136
"aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
137137
},
138138
"npm:aurelia-templating-binding@1.0.0-rc.1.0.0": {
139-
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
139+
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.2",
140140
"aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
141141
"aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0"
142142
},
143143
"npm:aurelia-templating-resources@1.0.0-rc.1.0.0": {
144-
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
144+
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.2",
145145
"aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
146146
"aurelia-loader": "npm:aurelia-loader@1.0.0-rc.1.0.0",
147147
"aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
@@ -161,7 +161,7 @@ System.config({
161161
"aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0"
162162
},
163163
"npm:aurelia-templating@1.0.0-rc.1.0.0": {
164-
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
164+
"aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.2",
165165
"aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
166166
"aurelia-loader": "npm:aurelia-loader@1.0.0-rc.1.0.0",
167167
"aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
define(['exports', './virtual-repeat'], function (exports, _virtualRepeat) {
1+
define(['exports', './virtual-repeat', './virtual-repeat-next'], function (exports, _virtualRepeat, _virtualRepeatNext) {
22
'use strict';
33

44
Object.defineProperty(exports, "__esModule", {
55
value: true
66
});
7-
exports.VirtualRepeat = undefined;
7+
exports.VirtualRepeatNext = exports.VirtualRepeat = undefined;
88
exports.configure = configure;
99
function configure(config) {
10-
config.globalResources('./virtual-repeat');
10+
config.globalResources('./virtual-repeat', './virtual-repeat-next');
1111
}
1212

1313
exports.VirtualRepeat = _virtualRepeat.VirtualRepeat;
14+
exports.VirtualRepeatNext = _virtualRepeatNext.VirtualRepeatNext;
1415
});

dist/amd/template-strategy.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ define(['exports', 'aurelia-pal', 'aurelia-templating', './utilities'], function
4646
};
4747

4848
TableStrategy.prototype.moveViewFirst = function moveViewFirst(view, topBuffer) {
49-
(0, _utilities.insertBeforeNode)(view, _aureliaPal.DOM.nextElementSibling(topBuffer.parentNode).previousSibling);
49+
(0, _utilities.insertBeforeNode)(view, _aureliaPal.DOM.nextElementSibling(topBuffer.parentNode));
5050
};
5151

5252
TableStrategy.prototype.moveViewLast = function moveViewLast(view, bottomBuffer) {
53-
(0, _utilities.insertBeforeNode)(view, bottomBuffer.parentNode);
53+
var previousSibling = bottomBuffer.parentNode.previousSibling;
54+
var referenceNode = previousSibling.nodeType === 8 && previousSibling.data === 'anchor' ? previousSibling : bottomBuffer.parentNode;
55+
(0, _utilities.insertBeforeNode)(view, referenceNode);
5456
};
5557

5658
TableStrategy.prototype.createTopBufferElement = function createTopBufferElement(element) {

dist/amd/virtual-repeat-next.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
define(['exports', 'aurelia-templating'], function (exports, _aureliaTemplating) {
2+
'use strict';
3+
4+
Object.defineProperty(exports, "__esModule", {
5+
value: true
6+
});
7+
exports.VirtualRepeatNext = undefined;
8+
9+
10+
11+
var _dec, _class;
12+
13+
var VirtualRepeatNext = exports.VirtualRepeatNext = (_dec = (0, _aureliaTemplating.customAttribute)('virtual-repeat-next'), _dec(_class = function () {
14+
function VirtualRepeatNext() {
15+
16+
}
17+
18+
VirtualRepeatNext.prototype.attached = function attached() {};
19+
20+
VirtualRepeatNext.prototype.bind = function bind(bindingContext, overrideContext) {
21+
this.scope = { bindingContext: bindingContext, overrideContext: overrideContext };
22+
};
23+
24+
return VirtualRepeatNext;
25+
}()) || _class);
26+
});

dist/amd/virtual-repeat.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
66
});
77
exports.VirtualRepeat = undefined;
88

9+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
10+
return typeof obj;
11+
} : function (obj) {
12+
return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
13+
};
14+
915
function _initDefineProp(target, property, descriptor, context) {
1016
if (!descriptor) return;
1117
Object.defineProperty(target, property, {
@@ -103,6 +109,7 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
103109
_this._fixedHeightContainer = false;
104110
_this._hasCalculatedSizes = false;
105111
_this._isAtTop = true;
112+
_this._calledGetMore = false;
106113

107114
_initDefineProp(_this, 'items', _descriptor, _this);
108115

@@ -276,6 +283,7 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
276283
this._lastRebind = this._first;
277284
var movedViewsCount = this._moveViews(viewsToMove);
278285
var adjustHeight = movedViewsCount < viewsToMove ? this._bottomBufferHeight : itemHeight * movedViewsCount;
286+
this._getMore();
279287
this._switchedDirection = false;
280288
this._topBufferHeight = this._topBufferHeight + adjustHeight;
281289
this._bottomBufferHeight = this._bottomBufferHeight - adjustHeight;
@@ -308,6 +316,45 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
308316
this._ticking = false;
309317
};
310318

319+
VirtualRepeat.prototype._getMore = function _getMore() {
320+
var _this5 = this;
321+
322+
if (this.isLastIndex) {
323+
if (!this._calledGetMore) {
324+
var _ret = function () {
325+
var getMoreFunc = _this5.view(0).firstChild.getAttribute('virtual-repeat-next');
326+
if (!getMoreFunc) {
327+
return {
328+
v: void 0
329+
};
330+
}
331+
var getMore = _this5.scope.overrideContext.bindingContext[getMoreFunc];
332+
333+
_this5.observerLocator.taskQueue.queueMicroTask(function () {
334+
_this5._calledGetMore = true;
335+
if (getMore instanceof Promise) {
336+
return getMore.then(function () {
337+
_this5._calledGetMore = false;
338+
});
339+
} else if (typeof getMore === 'function') {
340+
var result = getMore.bind(_this5.scope.overrideContext.bindingContext)();
341+
if (result instanceof Promise) {
342+
return result.then(function () {
343+
_this5._calledGetMore = false;
344+
});
345+
} else {
346+
_this5._calledGetMore = false;
347+
return;
348+
}
349+
}
350+
});
351+
}();
352+
353+
if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
354+
}
355+
}
356+
};
357+
311358
VirtualRepeat.prototype._checkScrolling = function _checkScrolling() {
312359
if (this._first > this._previousFirst && (this._bottomBufferHeight > 0 || !this.isLastIndex)) {
313360
if (!this._scrollingDown) {
@@ -346,15 +393,15 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
346393
};
347394

348395
VirtualRepeat.prototype._moveViews = function _moveViews(length) {
349-
var _this5 = this;
396+
var _this6 = this;
350397

351398
var getNextIndex = this._scrollingDown ? function (index, i) {
352399
return index + i;
353400
} : function (index, i) {
354401
return index - i;
355402
};
356403
var isAtFirstOrLastIndex = function isAtFirstOrLastIndex() {
357-
return _this5._scrollingDown ? _this5.isLastIndex : _this5._isAtTop;
404+
return _this6._scrollingDown ? _this6.isLastIndex : _this6._isAtTop;
358405
};
359406
var childrenLength = this.viewCount();
360407
var viewIndex = this._scrollingDown ? 0 : childrenLength - 1;

dist/aurelia-ui-virtualization.d.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
import {
2+
customAttribute,
3+
View,
4+
BoundViewFactory,
5+
ViewSlot,
6+
ViewResources,
7+
TargetInstruction,
8+
bindable,
9+
templateController
10+
} from 'aurelia-templating';
111
import {
212
updateOverrideContext,
313
ArrayRepeatStrategy,
@@ -10,16 +20,6 @@ import {
1020
updateOneTimeBinding,
1121
viewsRequireLifecycle
1222
} from 'aurelia-templating-resources';
13-
import {
14-
View,
15-
BoundViewFactory,
16-
ViewSlot,
17-
ViewResources,
18-
TargetInstruction,
19-
customAttribute,
20-
bindable,
21-
templateController
22-
} from 'aurelia-templating';
2323
import {
2424
DOM
2525
} from 'aurelia-pal';
@@ -43,6 +43,13 @@ export declare class DomHelper {
4343
getElementDistanceToTopOfDocument(element: Element): number;
4444
hasOverflowScroll(element: Element): boolean;
4545
}
46+
47+
//Placeholder attribute to prohibit use of this attribute name in other places
48+
export declare class VirtualRepeatNext {
49+
constructor();
50+
attached(): any;
51+
bind(bindingContext?: any, overrideContext?: any): void;
52+
}
4653
export declare function calcOuterHeight(element: Element): number;
4754
export declare function insertBeforeNode(view: View, bottomBuffer: number): void;
4855

0 commit comments

Comments
 (0)