mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(collectionRepeat): other children of ion-content element fit in
Closes #1920. Closes #1866. Closes #1380.
This commit is contained in:
46
js/angular/directive/collectionRepeat.js
vendored
46
js/angular/directive/collectionRepeat.js
vendored
@@ -22,13 +22,10 @@
|
||||
* Pixel amounts or percentages are allowed (see below).
|
||||
* 3. The elements rendered will be absolutely positioned: be sure to let your CSS work with
|
||||
* this (see below).
|
||||
* 4. Keep the HTML of your repeated elements as simple as possible.
|
||||
* The more complicated your elements, the more likely it is that the on-demand compilation will cause
|
||||
* some jerkiness in the user's scrolling.
|
||||
* 6. Each collection-repeat list will take up all of its parent scrollView's space.
|
||||
* 4. Each collection-repeat list will take up all of its parent scrollView's space.
|
||||
* If you wish to have multiple lists on one page, put each list within its own
|
||||
* {@link ionic.directive:ionScroll ionScroll} container.
|
||||
* 7. You should not use the ng-show and ng-hide directives on your ion-content/ion-scroll elements that
|
||||
* 5. You should not use the ng-show and ng-hide directives on your ion-content/ion-scroll elements that
|
||||
* have a collection-repeat inside. ng-show and ng-hide apply the `display: none` css rule to the content's
|
||||
* style, causing the scrollView to read the width and height of the content as 0. Resultingly,
|
||||
* collection-repeat will render elements that have just been un-hidden incorrectly.
|
||||
@@ -154,6 +151,10 @@ function($collectionRepeatManager, $collectionDataSource, $parse) {
|
||||
require: '^$ionicScroll',
|
||||
controller: [function(){}],
|
||||
link: function($scope, $element, $attr, scrollCtrl, $transclude) {
|
||||
var wrap = jqLite('<div style="position:relative;">');
|
||||
$element.parent()[0].insertBefore(wrap[0], $element[0]);
|
||||
wrap.append($element);
|
||||
|
||||
var scrollView = scrollCtrl.scrollView;
|
||||
if (scrollView.options.scrollingX && scrollView.options.scrollingY) {
|
||||
throw new Error(COLLECTION_REPEAT_SCROLLVIEW_XY_ERROR);
|
||||
@@ -216,9 +217,32 @@ function($collectionRepeatManager, $collectionDataSource, $parse) {
|
||||
rerender(value);
|
||||
});
|
||||
|
||||
var scrollViewContent = scrollCtrl.scrollView.__content;
|
||||
function rerender(value) {
|
||||
var beforeSiblings = [];
|
||||
var afterSiblings = [];
|
||||
var before = true;
|
||||
forEach(scrollViewContent.children, function(node, i) {
|
||||
if ( ionic.DomUtil.elementIsDescendant($element[0], node, scrollViewContent) ) {
|
||||
before = false;
|
||||
} else {
|
||||
var width = node.offsetWidth;
|
||||
var height = node.offsetHeight;
|
||||
if (width && height) {
|
||||
var element = jqLite(node);
|
||||
(before ? beforeSiblings : afterSiblings).push({
|
||||
width: node.offsetWidth,
|
||||
height: node.offsetHeight,
|
||||
element: element,
|
||||
scope: element.isolateScope() || element.scope(),
|
||||
isOutside: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scrollView.resize();
|
||||
dataSource.setData(value);
|
||||
dataSource.setData(value, beforeSiblings, afterSiblings);
|
||||
collectionRepeatManager.resize();
|
||||
}
|
||||
function onWindowResize() {
|
||||
@@ -237,7 +261,7 @@ function($collectionRepeatManager, $collectionDataSource, $parse) {
|
||||
}]);
|
||||
|
||||
// Fix for #1674
|
||||
// Problem: if an ngSrc or ngHref expression evaluates to a falsy value, it will
|
||||
// Problem: if an ngSrc or ngHref expression evaluates to a falsy value, it will
|
||||
// not erase the previous truthy value of the href.
|
||||
// In collectionRepeat, we re-use elements from before. So if the ngHref expression
|
||||
// evaluates to truthy for item 1 and then falsy for item 2, if an element changes
|
||||
@@ -248,13 +272,13 @@ function collectionRepeatSrcDirective(ngAttrName, attrName) {
|
||||
return [function() {
|
||||
return {
|
||||
priority: '99', // it needs to run after the attributes are interpolated
|
||||
require: '^?collectionRepeat',
|
||||
link: function(scope, element, attr, collectionRepeatCtrl) {
|
||||
if (!collectionRepeatCtrl) return;
|
||||
attr.$observe(ngAttrName, function(value) {
|
||||
if (!value) {
|
||||
element.removeAttr(attrName);
|
||||
}
|
||||
element[0][attr] = '';
|
||||
setTimeout(function() {
|
||||
element[0][attr] = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
14
js/angular/directive/infiniteScroll.js
vendored
14
js/angular/directive/infiniteScroll.js
vendored
@@ -60,24 +60,19 @@ IonicModule
|
||||
.directive('ionInfiniteScroll', ['$timeout', function($timeout) {
|
||||
function calculateMaxValue(distance, maximum, isPercent) {
|
||||
return isPercent ?
|
||||
maximum * (1 - parseInt(distance,10) / 100) :
|
||||
maximum - parseInt(distance, 10);
|
||||
maximum * (1 - parseFloat(distance,10) / 100) :
|
||||
maximum - parseFloat(distance, 10);
|
||||
}
|
||||
return {
|
||||
restrict: 'E',
|
||||
require: ['^$ionicScroll', 'ionInfiniteScroll'],
|
||||
template:
|
||||
'<div class="scroll-infinite">' +
|
||||
'<div class="scroll-infinite-content">' +
|
||||
'<i class="icon {{icon()}} icon-refreshing"></i>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
template: '<i class="icon {{icon()}} icon-refreshing"></i>',
|
||||
scope: true,
|
||||
controller: ['$scope', '$attrs', function($scope, $attrs) {
|
||||
this.isLoading = false;
|
||||
this.scrollView = null; //given by link function
|
||||
this.getMaxScroll = function() {
|
||||
var distance = ($attrs.distance || '1%').trim();
|
||||
var distance = ($attrs.distance || '2.5%').trim();
|
||||
var isPercent = distance.indexOf('%') !== -1;
|
||||
var maxValues = this.scrollView.getScrollMax();
|
||||
return {
|
||||
@@ -109,6 +104,7 @@ IonicModule
|
||||
$element[0].classList.remove('active');
|
||||
$timeout(function() {
|
||||
scrollView.resize();
|
||||
checkBounds();
|
||||
}, 0, false);
|
||||
infiniteScrollCtrl.isLoading = false;
|
||||
};
|
||||
|
||||
34
js/angular/service/collectionRepeatDataSource.js
vendored
34
js/angular/service/collectionRepeatDataSource.js
vendored
@@ -4,12 +4,16 @@ IonicModule
|
||||
'$parse',
|
||||
'$rootScope',
|
||||
function($cacheFactory, $parse, $rootScope) {
|
||||
function hideWithTransform(element) {
|
||||
element.css(ionic.CSS.TRANSFORM, 'translate3d(-2000px,-2000px,0)');
|
||||
}
|
||||
|
||||
function CollectionRepeatDataSource(options) {
|
||||
var self = this;
|
||||
this.scope = options.scope;
|
||||
this.transcludeFn = options.transcludeFn;
|
||||
this.transcludeParent = options.transcludeParent;
|
||||
this.element = options.element;
|
||||
|
||||
this.keyExpr = options.keyExpr;
|
||||
this.listExpr = options.listExpr;
|
||||
@@ -61,6 +65,8 @@ function($cacheFactory, $parse, $rootScope) {
|
||||
height: this.heightGetter(this.scope, locals)
|
||||
};
|
||||
}, this);
|
||||
this.dimensions = this.beforeSiblings.concat(this.dimensions).concat(this.afterSiblings);
|
||||
this.dataStartIndex = this.beforeSiblings.length;
|
||||
},
|
||||
createItem: function() {
|
||||
var item = {};
|
||||
@@ -87,6 +93,13 @@ function($cacheFactory, $parse, $rootScope) {
|
||||
},
|
||||
attachItemAtIndex: function(index) {
|
||||
var value = this.data[index];
|
||||
|
||||
if (index < this.dataStartIndex) {
|
||||
return this.beforeSiblings[index];
|
||||
} else if (index > this.data.length) {
|
||||
return this.afterSiblings[index - this.data.length - this.dataStartIndex];
|
||||
}
|
||||
|
||||
var hash = this.itemHashGetter(index, value);
|
||||
var item = this.getItem(hash);
|
||||
|
||||
@@ -118,23 +131,36 @@ function($cacheFactory, $parse, $rootScope) {
|
||||
detachItem: function(item) {
|
||||
delete this.attachedItems[item.hash];
|
||||
|
||||
//If it's an outside item, only hide it. These items aren't part of collection
|
||||
//repeat's list, only sit outside
|
||||
if (item.isOutside) {
|
||||
hideWithTransform(item.element);
|
||||
|
||||
// If we are at the limit of backup items, just get rid of the this element
|
||||
if (this.backupItemsArray.length >= this.BACKUP_ITEMS_LENGTH) {
|
||||
} else if (this.backupItemsArray.length >= this.BACKUP_ITEMS_LENGTH) {
|
||||
this.destroyItem(item);
|
||||
// Otherwise, add it to our backup items
|
||||
} else {
|
||||
this.backupItemsArray.push(item);
|
||||
item.element.css(ionic.CSS.TRANSFORM, 'translate3d(-2000px,-2000px,0)');
|
||||
hideWithTransform(item.element);
|
||||
//Don't .$destroy(), just stop watchers and events firing
|
||||
disconnectScope(item.scope);
|
||||
}
|
||||
|
||||
},
|
||||
getLength: function() {
|
||||
return this.data && this.data.length || 0;
|
||||
return this.dimensions && this.dimensions.length || 0;
|
||||
},
|
||||
setData: function(value) {
|
||||
setData: function(value, beforeSiblings, afterSiblings) {
|
||||
this.data = value || [];
|
||||
this.beforeSiblings = beforeSiblings || [];
|
||||
this.afterSiblings = afterSiblings || [];
|
||||
this.calculateDataDimensions();
|
||||
|
||||
this.afterSiblings.forEach(function(item) {
|
||||
item.element.css({position: 'absolute', top: '0', left: '0' });
|
||||
hideWithTransform(item.element);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
57
js/angular/service/collectionRepeatManager.js
vendored
57
js/angular/service/collectionRepeatManager.js
vendored
@@ -100,7 +100,24 @@ function($rootScope, $timeout) {
|
||||
var secondaryScrollSize = this.secondaryScrollSize();
|
||||
var previousItem;
|
||||
|
||||
return this.dataSource.dimensions.map(function(dim) {
|
||||
this.dataSource.beforeSiblings && this.dataSource.beforeSiblings.forEach(calculateSize, this);
|
||||
var beforeSize = primaryPos + (previousItem ? previousItem.primarySize : 0);
|
||||
|
||||
primaryPos = secondaryPos = 0;
|
||||
previousItem = null;
|
||||
|
||||
|
||||
var dimensions = this.dataSource.dimensions.map(calculateSize, this);
|
||||
var totalSize = primaryPos + (previousItem ? previousItem.primarySize : 0);
|
||||
|
||||
return {
|
||||
beforeSize: beforeSize,
|
||||
totalSize: totalSize,
|
||||
dimensions: dimensions
|
||||
};
|
||||
|
||||
function calculateSize(dim) {
|
||||
|
||||
//Each dimension is an object {width: Number, height: Number} provided by
|
||||
//the dataSource
|
||||
var rect = {
|
||||
@@ -129,12 +146,13 @@ function($rootScope, $timeout) {
|
||||
|
||||
previousItem = rect;
|
||||
return rect;
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
resize: function() {
|
||||
this.dimensions = this.calculateDimensions();
|
||||
var lastItem = this.dimensions[this.dimensions.length - 1];
|
||||
this.viewportSize = lastItem ? lastItem.primaryPos + lastItem.primarySize : 0;
|
||||
var result = this.calculateDimensions();
|
||||
this.dimensions = result.dimensions;
|
||||
this.viewportSize = result.totalSize;
|
||||
this.beforeSize = result.beforeSize;
|
||||
this.setCurrentIndex(0);
|
||||
this.render(true);
|
||||
if (!this.dataSource.backupItemsArray.length) {
|
||||
@@ -219,6 +237,7 @@ function($rootScope, $timeout) {
|
||||
* the data source to render the correct items into the DOM.
|
||||
*/
|
||||
render: function(shouldRedrawAll) {
|
||||
var self = this;
|
||||
var i;
|
||||
var isOutOfBounds = ( this.currentIndex >= this.dataSource.getLength() );
|
||||
// We want to remove all the items and redraw everything if we're out of bounds
|
||||
@@ -258,10 +277,12 @@ function($rootScope, $timeout) {
|
||||
// Keep rendering items, adding them until we are past the end of the visible scroll area
|
||||
i = renderStartIndex;
|
||||
while ((rect = this.dimensions[i]) && (rect.primaryPos - rect.primarySize < scrollSizeEnd)) {
|
||||
this.renderItem(i, rect.primaryPos, rect.secondaryPos);
|
||||
i++;
|
||||
doRender(i++);
|
||||
}
|
||||
var renderEndIndex = i - 1;
|
||||
//Add two more items at the end
|
||||
doRender(i++);
|
||||
doRender(i);
|
||||
var renderEndIndex = i;
|
||||
|
||||
// Remove any items that were rendered and aren't visible anymore
|
||||
for (i in this.renderedItems) {
|
||||
@@ -271,6 +292,17 @@ function($rootScope, $timeout) {
|
||||
}
|
||||
|
||||
this.setCurrentIndex(startIndex);
|
||||
|
||||
function doRender(dataIndex) {
|
||||
var rect = self.dimensions[dataIndex];
|
||||
if (!rect) {
|
||||
|
||||
}else if (dataIndex < self.dataSource.dataStartIndex) {
|
||||
// do nothing
|
||||
} else {
|
||||
self.renderItem(dataIndex, rect.primaryPos - self.beforeSize, rect.secondaryPos);
|
||||
}
|
||||
}
|
||||
},
|
||||
renderItem: function(dataIndex, primaryPos, secondaryPos) {
|
||||
// Attach an item, and set its transform position to the required value
|
||||
@@ -302,6 +334,15 @@ function($rootScope, $timeout) {
|
||||
}
|
||||
};
|
||||
|
||||
var exceptions = {'renderScroll':1, 'renderIfNeeded':1};
|
||||
forEach(CollectionRepeatManager.prototype, function(method, key) {
|
||||
if (exceptions[key]) return;
|
||||
CollectionRepeatManager.prototype[key] = function() {
|
||||
console.log(key + '(', arguments, ')');
|
||||
return method.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
return CollectionRepeatManager;
|
||||
}]);
|
||||
|
||||
|
||||
@@ -210,6 +210,15 @@
|
||||
});
|
||||
},
|
||||
|
||||
elementIsDescendant: function(el, parent, stopAt) {
|
||||
var current = el;
|
||||
do {
|
||||
if (current === parent) return true;
|
||||
current = current.parentNode;
|
||||
} while (current && current !== stopAt);
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ionic.DomUtil#getParentWithClass
|
||||
|
||||
@@ -245,36 +245,27 @@ body.grade-c {
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-refresher-content {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: $scroll-refresh-icon-color;
|
||||
text-align: center;
|
||||
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
// Infinite scroll
|
||||
ion-infinite-scroll .scroll-infinite {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-top: -70px;
|
||||
ion-infinite-scroll {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.scroll-infinite-content {
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
font-size: 30px; }
|
||||
opacity: 0;
|
||||
display: block;
|
||||
|
||||
ion-infinite-scroll.active .scroll-infinite {
|
||||
margin-top: -30px;
|
||||
@include transition(opacity 0.25s);
|
||||
@include display-flex();
|
||||
@include flex-direction(row);
|
||||
@include justify-content(center);
|
||||
@include align-items(center);
|
||||
|
||||
.icon {
|
||||
color: #666666;
|
||||
font-size: 30px;
|
||||
color: $scroll-refresh-icon-color;
|
||||
}
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-scroll {
|
||||
|
||||
@@ -26,12 +26,11 @@
|
||||
<script>
|
||||
angular.module('ionicApp', ['ionic'])
|
||||
|
||||
.controller('MyCtrl', ['$scope', function($scope) {
|
||||
.controller('MyCtrl', ['$scope', '$timeout', function($scope, $timeout) {
|
||||
|
||||
$scope.data = { items: [] };
|
||||
|
||||
var fetchItems = function() {
|
||||
console.log('Pushing item');
|
||||
$scope.data.items.push({
|
||||
title: 'Item ' + $scope.data.items.length
|
||||
});
|
||||
@@ -40,7 +39,7 @@
|
||||
};
|
||||
|
||||
$scope.onInfinite = function() {
|
||||
fetchItems();
|
||||
$timeout(fetchItems, 2000);
|
||||
};
|
||||
|
||||
for(var i = 0; i < 10; i++) {
|
||||
|
||||
@@ -18,65 +18,57 @@
|
||||
</a>
|
||||
</ion-header-bar>
|
||||
<ion-content>
|
||||
<p>
|
||||
Hi, I'm some text before the list.
|
||||
</p>
|
||||
<div class="card full">
|
||||
Hi, I'm a card before the list.
|
||||
</div>
|
||||
<ion-list>
|
||||
<ion-item
|
||||
class="item-avatar-left item-icon-right"
|
||||
ng-click="alert(item)"
|
||||
collection-repeat="item in items"
|
||||
collection-item-height="$index % 10 === 0 ? 500 : 85"
|
||||
collection-item-height="85"
|
||||
collection-item-width="'100%'"
|
||||
style="position: absolute; left: 0; right: 0;">
|
||||
<img ng-src="{{item.image}}">
|
||||
ng-style="{height: '85px'}"
|
||||
style="left: 0; right: 0;">
|
||||
<h2>{{item.text}}</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis porttitor diam urna, vitae consectetur lectus aliquet quis.</p>
|
||||
<i class="icon" style="color:red; font-size: 30px;" ng-class="['ion-ios7-person','ion-person','ion-android-contact','ion-android-social-user','ion-person-stalker'][$index % 5]"></i>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<ion-infinite-scroll on-infinite="loadMore()"></ion-infinite-scroll>
|
||||
<div class="card full">
|
||||
Hi, I'm a card after the list.
|
||||
</div>
|
||||
</ion-content>
|
||||
<script>
|
||||
var dataUris = {};
|
||||
function convertImgToBase64(url, callback, outputFormat){
|
||||
var canvas = document.createElement('CANVAS'),
|
||||
ctx = canvas.getContext('2d'),
|
||||
img = new Image;
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onload = function(){
|
||||
var dataURL;
|
||||
canvas.height = img.height;
|
||||
canvas.width = img.width;
|
||||
ctx.drawImage(img,0,0);
|
||||
dataURL = canvas.toDataURL(outputFormat || 'image/png');
|
||||
callback.call(this, dataURL);
|
||||
canvas = null;
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
function MainCtrl($scope, $ionicScrollDelegate, $timeout, $q, $ionicLoading) {
|
||||
var images = [];
|
||||
|
||||
$ionicLoading.show({
|
||||
template: 'Loading images...'
|
||||
});
|
||||
var deferred;
|
||||
for (var i = 0; i < 5; i++) {
|
||||
deferred = $q.defer();
|
||||
convertImgToBase64('http://placekitten.com/'+(40+(10*i))+'/'+(40+(10*i)), deferred.resolve);
|
||||
images.push(deferred.promise);
|
||||
$scope.items = [];
|
||||
function addImage() {
|
||||
var i = $scope.items.length;
|
||||
$scope.items.push({
|
||||
text: 'Item ' + i,
|
||||
image: 'http://placekitten.com/'+(100+50%i)+'/'+(100+50%i)
|
||||
});
|
||||
}
|
||||
|
||||
$q.all(images).then(function(dataUrls) {
|
||||
$scope.items = [];
|
||||
for (var item = 0; item < 5000; item++) {
|
||||
$scope.items.push({
|
||||
text: 'Item ' + item,
|
||||
image: dataUrls[item % 5]
|
||||
});
|
||||
}
|
||||
$timeout($ionicLoading.hide, 200);
|
||||
});
|
||||
for (var i = 0; i < 20; i++) addImage();
|
||||
|
||||
$scope.scrollBottom = $ionicScrollDelegate.scrollBottom;
|
||||
|
||||
$scope.loadMore = function() {
|
||||
$timeout(function() {
|
||||
var n = 1 + Math.floor(4*Math.random());
|
||||
for (var i = 0; i < n; i++) addImage();
|
||||
$scope.$broadcast('scroll.infiniteScrollComplete');
|
||||
}, 1500);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.full {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -106,16 +106,16 @@ describe('collectionRepeat directive', function() {
|
||||
|
||||
it('should error if list is not an array and is truthy', function() {
|
||||
var el = setup('collection-repeat="item in items" collection-item-height="50"');
|
||||
expect(function() {
|
||||
expect(function() {
|
||||
el.scope().$apply('items = "string"');
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
expect(function() {
|
||||
el.scope().$apply('items = 123');
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
expect(function() {
|
||||
el.scope().$apply('items = {}');
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
expect(function() {
|
||||
el.scope().$apply('items = []');
|
||||
}).not.toThrow();
|
||||
});
|
||||
@@ -128,11 +128,11 @@ describe('collectionRepeat directive', function() {
|
||||
repeatManager.resize.reset();
|
||||
|
||||
el.scope().$apply('items = [ 1,2,3 ]');
|
||||
expect(dataSource.setData).toHaveBeenCalledWith(el.scope().items);
|
||||
expect(dataSource.setData).toHaveBeenCalledWith(el.scope().items, [], []);
|
||||
expect(repeatManager.resize.callCount).toBe(1);
|
||||
expect(scrollView.resize.callCount).toBe(1);
|
||||
el.scope().$apply('items = null');
|
||||
expect(dataSource.setData).toHaveBeenCalledWith(null);
|
||||
expect(dataSource.setData).toHaveBeenCalledWith(null, [], []);
|
||||
expect(repeatManager.resize.callCount).toBe(2);
|
||||
expect(scrollView.resize.callCount).toBe(2);
|
||||
});
|
||||
@@ -147,7 +147,7 @@ describe('collectionRepeat directive', function() {
|
||||
el.scope().items = [1,2,3];
|
||||
|
||||
ionic.trigger('resize', { target: window });
|
||||
expect(dataSource.setData).toHaveBeenCalledWith(el.scope().items);
|
||||
expect(dataSource.setData).toHaveBeenCalledWith(el.scope().items, [], []);
|
||||
expect(repeatManager.resize.callCount).toBe(1);
|
||||
expect(scrollView.resize.callCount).toBe(1);
|
||||
});
|
||||
|
||||
@@ -98,11 +98,11 @@ describe('ionicInfiniteScroll directive', function() {
|
||||
].forEach(function(opts) {
|
||||
|
||||
describe('with scrollingX='+opts.scrollingX+', scrollingY='+opts.scrollingY, function() {
|
||||
it('should default to 1%', function() {
|
||||
it('should default to 2.5%', function() {
|
||||
var el = setup('', {}, opts);
|
||||
expect(ctrl.getMaxScroll()).toEqual({
|
||||
left: opts.scrollingX ? scrollLeftMaxValue * 0.99 : -1,
|
||||
top: opts.scrollingY ? scrollTopMaxValue * 0.99 : -1
|
||||
left: opts.scrollingX ? scrollLeftMaxValue * 0.975 : -1,
|
||||
top: opts.scrollingY ? scrollTopMaxValue * 0.975 : -1
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ describe('$collectionDataSource service', function() {
|
||||
cb( $compile(template || '<div>')(scope) );
|
||||
};
|
||||
dataSource = new $collectionDataSource(options);
|
||||
dataSource.dataStartIndex = 0;
|
||||
dataSource.beforeSiblings = [];
|
||||
dataSource.afterSiblings = [];
|
||||
});
|
||||
return dataSource;
|
||||
}
|
||||
@@ -148,6 +151,7 @@ describe('$collectionDataSource service', function() {
|
||||
keyExpr: 'value'
|
||||
});
|
||||
source.data = ['a', 'b', 'c'];
|
||||
source.dimensions = ['a','b','c'];
|
||||
spyOn(source, 'getItem').andCallFake(function() {
|
||||
return { scope: $rootScope.$new() };
|
||||
});
|
||||
@@ -242,15 +246,15 @@ describe('$collectionDataSource service', function() {
|
||||
describe('.getLength()', function() {
|
||||
it('should return 0 by default', function() {
|
||||
var source = setup();
|
||||
source.data = null;
|
||||
source.dimensions = null;
|
||||
expect(source.getLength()).toBe(0);
|
||||
});
|
||||
|
||||
it('should return data length', function() {
|
||||
it('should return dimensions length', function() {
|
||||
var source = setup();
|
||||
source.data = [1,2,3];
|
||||
source.dimensions = [1,2,3];
|
||||
expect(source.getLength()).toBe(3);
|
||||
source.data = [];
|
||||
source.dimensions = [];
|
||||
expect(source.getLength()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -180,7 +180,7 @@ describe('collectionRepeatManager service', function() {
|
||||
manager.secondaryScrollSize = function() {
|
||||
return 100;
|
||||
};
|
||||
var result = manager.calculateDimensions();
|
||||
var result = manager.calculateDimensions().dimensions;
|
||||
expect(result[0].primarySize).toBe(20);
|
||||
expect(result[0].secondarySize).toBe(100);
|
||||
expect(result[0].primaryPos).toBe(0);
|
||||
@@ -214,7 +214,7 @@ describe('collectionRepeatManager service', function() {
|
||||
manager.secondaryScrollSize = function() {
|
||||
return 90;
|
||||
};
|
||||
var result = manager.calculateDimensions();
|
||||
var result = manager.calculateDimensions().dimensions;
|
||||
expect(result[0].primarySize).toBe(30);
|
||||
expect(result[0].secondarySize).toBe(30);
|
||||
expect(result[0].primaryPos).toBe(0);
|
||||
@@ -251,7 +251,11 @@ describe('collectionRepeatManager service', function() {
|
||||
it('should work without data', function() {
|
||||
var manager = setup();
|
||||
spyOn(manager, 'render');
|
||||
spyOn(manager, 'calculateDimensions').andReturn([]);
|
||||
spyOn(manager, 'calculateDimensions').andReturn({
|
||||
dimensions: [],
|
||||
beforeSize: 0,
|
||||
totalSize: 0
|
||||
});
|
||||
spyOn(manager, 'setCurrentIndex');
|
||||
manager.resize();
|
||||
expect(manager.dimensions).toEqual([]);
|
||||
@@ -261,9 +265,13 @@ describe('collectionRepeatManager service', function() {
|
||||
});
|
||||
it('should work with data', function() {
|
||||
var manager = setup();
|
||||
spyOn(manager, 'calculateDimensions').andReturn([{
|
||||
primaryPos: 100, primarySize: 30
|
||||
}]);
|
||||
spyOn(manager, 'calculateDimensions').andReturn({
|
||||
dimensions: [{
|
||||
primaryPos: 100, primarySize: 30
|
||||
}],
|
||||
beforeSize: 0,
|
||||
totalSize: 130
|
||||
});
|
||||
manager.resize();
|
||||
expect(manager.viewportSize).toBe(130);
|
||||
});
|
||||
@@ -430,12 +438,12 @@ describe('collectionRepeatManager service', function() {
|
||||
});
|
||||
manager.resize(); //triggers render
|
||||
|
||||
//it should render (items that fit * items per row) with one extra row at end
|
||||
expect(Object.keys(manager.renderedItems).length).toBe(18);
|
||||
for (var i = 0; i < 18; i++) {
|
||||
//it should render (items that fit * items per row) with three extra row at end
|
||||
expect(Object.keys(manager.renderedItems).length).toBe(20);
|
||||
for (var i = 0; i < 20; i++) {
|
||||
expect(manager.renderedItems[i]).toBe(true);
|
||||
}
|
||||
expect(manager.renderedItems[18]).toBeUndefined();
|
||||
expect(manager.renderedItems[20]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should render items in the middle of the screen', function() {
|
||||
@@ -449,32 +457,15 @@ describe('collectionRepeatManager service', function() {
|
||||
manager.resize();
|
||||
var startIndex = 17;
|
||||
var bufferStartIndex = 14; //one row of buffer before the start
|
||||
var bufferEndIndex = 35; //start + 17 + 6
|
||||
var bufferEndIndex = 37; //start + 17 + 6
|
||||
|
||||
expect(Object.keys(manager.renderedItems).length).toBe(22);
|
||||
expect(Object.keys(manager.renderedItems).length).toBe(24);
|
||||
for (var i = bufferStartIndex; i <= bufferEndIndex; i++) {
|
||||
expect(manager.renderedItems[i]).toBe(true);
|
||||
}
|
||||
expect(manager.renderedItems[bufferStartIndex - 1]).toBeUndefined();
|
||||
expect(manager.renderedItems[bufferEndIndex + 1]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should remove items outside the range', function() {
|
||||
var manager = mockRendering({
|
||||
itemWidth: 3,
|
||||
itemHeight: 20,
|
||||
scrollWidth: 10,
|
||||
scrollHeight: 100
|
||||
});
|
||||
manager.resize();
|
||||
manager.removeItem.reset();
|
||||
manager.renderedItems = { 17: true, 18: true, 19: true };
|
||||
//resize() re-renders everything, need to just do a normal rerender
|
||||
manager.render();
|
||||
expect(manager.removeItem.callCount).toBe(2);
|
||||
expect(manager.removeItem).toHaveBeenCalledWith('18');
|
||||
expect(manager.removeItem).toHaveBeenCalledWith('19');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.renderItem()', function() {
|
||||
|
||||
Reference in New Issue
Block a user