-
-
Save Poordeveloper/e6a1714ea399f95c779f to your computer and use it in GitHub Desktop.
m.directive('ionSticky', ['$ionicPosition', '$compile', '$timeout', function ($ionicPosition, $compile, $timeout) { | |
return { | |
restrict: 'A', | |
require: '^$ionicScroll', | |
link: function ($scope, $element, $attr, $ionicScroll) { | |
var scroll = angular.element($ionicScroll.element); | |
var clone; | |
// creates the sticky clone and adds it to DOM | |
var createStickyClone = function ($element) { | |
clone = $element.clone().css({ | |
position: 'absolute', | |
top: $ionicPosition.position(scroll).top + "px", | |
left: 0, | |
right: 0 | |
}); | |
clone[0].className += " assertive"; | |
clone.removeAttr('ng-repeat-start'); | |
clone.removeAttr('ng-if'); | |
scroll.parent().append(clone); | |
// compile the clone so that anything in it is in Angular lifecycle. | |
$compile(clone)($scope); | |
}; | |
var removeStickyClone = function () { | |
if (clone) | |
clone.remove(); | |
clone = null; | |
}; | |
$scope.$on("$destroy", function () { | |
// remove the clone, unbind the scroll listener | |
removeStickyClone(); | |
angular.element($ionicScroll.element).off('scroll'); | |
}); | |
var lastActive; | |
var updateSticky = ionic.throttle(function() { | |
var active = null; | |
var dividers = []; | |
var tmp = $element[0].getElementsByClassName("item-divider"); | |
for (var i = 0; i < tmp.length; ++i) dividers.push(angular.element(tmp[i])); | |
if (dividers.length == 0) return; | |
if (!clone) createStickyClone(angular.element(dividers[0][0])) | |
dividers.sort(function(a, b) { return $ionicPosition.offset(a).top - $ionicPosition.offset(b).top; }); | |
var sctop = $ionicPosition.offset(scroll).top; | |
if ($ionicPosition.offset(dividers[0]).top - sctop - dividers[0].prop('offsetHeight') > 0) { | |
var letter = dividers[0][0].innerHTML.trim(); | |
var i = $scope.letters.indexOf(letter); | |
if (i == 0) return; | |
active = $scope.letters[i-1]; | |
} else for (var i = 0; i < dividers.length; ++i) { // can be changed to binary search | |
if ($ionicPosition.offset(dividers[i]).top - sctop - dividers[i].prop('offsetHeight') < 0) { // this equals to jquery outerHeight | |
if (i === dividers.length-1 || $ionicPosition.offset(dividers[i+1]).top - sctop - | |
(dividers[i].prop('offsetHeight') + dividers[i+1].prop('offsetHeight')) > 0) { | |
active = dividers[i][0].innerHTML.trim(); | |
break; | |
} | |
} | |
} | |
if (lastActive != active) { | |
if (active) clone[0].innerHTML = active; | |
if (lastActive) { | |
var e = scroll.parent()[0].getElementsByClassName(lastActive); | |
if (e && e[0]) e[0].className = e[0].className.replace(/assertive/g,''); | |
} | |
if (active) { | |
var e = scroll.parent()[0].getElementsByClassName(active); | |
if (e && e[0]) e[0].className += " assertive"; | |
} | |
lastActive = active; | |
} | |
}, 200); | |
scroll.on('scroll', function (event) { | |
updateSticky(); | |
}); | |
} | |
} | |
}]); |
Hello and thanks for this great directive !
Could you take a look at this ionic playground (similar to a jsfiddle or codepen, with HTML/JS/CSS tabs):
http://play.ionic.io/app/85ddaf5f1b64
I found your gist for the "collection-repeat" at https://gist.github.com/Poordeveloper/e6a1714ea399f95c779f
and used this code to define the "ion-sticky" directive (look at the JS tab, you'll find it under the 'MyCtrl' controller definition)
So here go the 2 problems I'm encountering:
- The directive throws an error when using "collection-repeat" to generate the list, which crashes the app
- The directive does NOT affect the list dividers, nor throws any errors when using "ng-repeat" to generate the list
Could you please check and let me know if this is a known issue to you (I see there are no issue on the github repo) ?
Thanx !
I tried this gist with collection-repeat but it has a bug :(.
For example, while scrolling through a Contacts list with last name's beginning with 'C', sometimes the sticky divider shows P, O, or R (Btw, this happens when I'm in the middle of the list of C's and the 'D' item-divider isn't anywhere near the top). I've used the original implementation with ng-repeat with the same data + HTML markup and it works perfectly fine so I'm fairly certain its not my code.
I did a do a bit of debugging with print statements. For the scenario I mentioned above, I noticed that the item-dividers that are supposed to contain 'C' are not in the list of dividers returned from $element[0].getElementsByClassName("item-divider"). It seems like the recycling nature of collection-repeat causes some rows to be deleted so there's no way for it to find the right item-divider.
@egamer106, I did get this bug too.
Is there a solution to this bug or is it near impossible to sort for collection-repeat?
Think I have a fix for this. Divider elements can sometimes be in the DOM but not actually be positioned in the list. You can see this because they have a transform: translate3d(-9999px, -9999px, 0px)
applied to the parent. If you exclude these elements from the dividers list, you'll only consider dividers that are actually visible.
Replace line 44:
for (var i = 0; i < tmp.length; ++i) dividers.push(angular.element(tmp[i]));
With:
for (var i = 0; i < tmp.length; ++i){
// Get the parent's transform value.
var elt = angular.element(tmp[i]);
var parentTransform = elt.parent().css('transform');
// Exclude dividers positioned out of the DOM
if (!parentTransform || parentTransform.indexOf("-9999") == -1){
dividers.push(elt);
}
}
i used element with ng-repeat its working, but it loops ng-repeat 8 times
That doesnt always work. collection-repeat has the capacity to add or remove elements to the dom as it scrolls up and gets 'too old'
My version: https://gist.github.com/bdirito/e25b8fafe7363a0662e17d3b96a61ffa
It assumes that if something got removed from the dom thats simply an artifact of collection-repeat and not a removal from the underlying repeat object (valid in my case). It has several other modifications for things that I was personally interested in. Its also in coffeescript with ngInject, requirejs(amd) and jquery dependencies. However it should get people an idea.
hi, the plugin works just great out of the box with collection-repeat with cache disabled, but when the view is cached it doesn't do the job anymore, i.e when i select an item in the list and then press the back button to return to the list, it is not effective.
Any suggestion about this ?
Please note that, it depends on $scope.letters which lists out all available dividers' innerHTML because colletion-repeat only renders visible list items. It only support simple dividers with the same style.