Skip to content

Commit

Permalink
feat(collectionRepeat): other children of ion-content element fit in
Browse files Browse the repository at this point in the history
Closes #1920. Closes #1866. Closes #1380.
  • Loading branch information
ajoslin committed Aug 6, 2014
1 parent c0b6426 commit 7ddb57e
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 146 deletions.
46 changes: 35 additions & 11 deletions js/angular/directive/collectionRepeat.js
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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() {
Expand All @@ -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
Expand All @@ -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;
});
});
}
};
Expand Down
14 changes: 5 additions & 9 deletions js/angular/directive/infiniteScroll.js
Expand Up @@ -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 {
Expand Down Expand Up @@ -109,6 +104,7 @@ IonicModule
$element[0].classList.remove('active');
$timeout(function() {
scrollView.resize();
checkBounds();
}, 0, false);
infiniteScrollCtrl.isLoading = false;
};
Expand Down
34 changes: 30 additions & 4 deletions js/angular/service/collectionRepeatDataSource.js
Expand Up @@ -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;
Expand Down Expand Up @@ -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 = {};
Expand All @@ -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);

Expand Down Expand Up @@ -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);
});
},
};

Expand Down
57 changes: 49 additions & 8 deletions js/angular/service/collectionRepeatManager.js
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

This comment has been minimized.

Copy link
@rbecheras

rbecheras Aug 28, 2014

Reviewing this file, intuitively, I focused on this changes, lines 260-264 of the old version, and 279-281 of the new one.
Maybe its not the issue at all, but it may be a clue.

//Add two more items at the end

This comment has been minimized.

Copy link
@rbecheras

rbecheras Aug 28, 2014

And here ... Its normal I don't understand because I don't know the internal complexity of this file, although It seems me a bit weird.

doRender(i++);
doRender(i);
var renderEndIndex = i;

// Remove any items that were rendered and aren't visible anymore
for (i in this.renderedItems) {
Expand All @@ -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
Expand Down Expand Up @@ -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;
}]);

9 changes: 9 additions & 0 deletions js/utils/dom.js
Expand Up @@ -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
Expand Down
43 changes: 17 additions & 26 deletions scss/_scaffolding.scss
Expand Up @@ -245,36 +245,27 @@ body.grade-c {
}
}

.scroll-refresher-content {
position: absolute;
bottom: 15px;
left: 0;
ion-infinite-scroll {
height: 60px;
width: 100%;
color: $scroll-refresh-icon-color;
text-align: center;

font-size: 30px;
}
opacity: 0;
display: block;

// Infinite scroll
ion-infinite-scroll .scroll-infinite {
position: relative;
overflow: hidden;
margin-top: -70px;
height: 60px;
}
@include transition(opacity 0.25s);
@include display-flex();
@include flex-direction(row);
@include justify-content(center);
@include align-items(center);

.scroll-infinite-content {
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
color: #666666;
text-align: center;
font-size: 30px; }
.icon {
color: #666666;
font-size: 30px;
color: $scroll-refresh-icon-color;
}

ion-infinite-scroll.active .scroll-infinite {
margin-top: -30px;
&.active {
opacity: 1;
}
}

.overflow-scroll {
Expand Down

0 comments on commit 7ddb57e

Please sign in to comment.