I have a scope variable $scope.first_unread_id which is defined in my controller. In my template, I have:
<div id="items" >
<ul class="standard-list">
<li ng-repeat="item in items" scroll-to-id="first_unread_id">
<span class="content">{{ item.content }}</span>
</li>
</ul>
</div>
and my directive looks like:
angular.module('ScrollToId', []).
directive('scrollToId', function () {
return function (scope, element, attributes) {
var id = scope.$parent[attributes["scrollToId"]];
if (id === scope.item.id) {
setTimeout(function () {
window.scrollTo(0, element[0].offsetTop - 100)
}, 20);
}
}
});
it works, however, two questions:
Is there a better way of getting the "first_unread_id" off the controller scope into the direct than interrogating scope.$parent? This seems a bit 'icky'. I was hoping I could pass that through the view to the direct as a parameter w/o having to repeat that on ever li element.
Is there a better way to avoid the need of the setTimeout() call? Without it, it works sometimes - I imagine due to difference in timing of layout. I understand the syntax I have used is defining a link function - but it isn't clear to me if that is a pre or post-link by default - and if that even matters for my issue.
Here are those 3 changes:
html:
<div id="items" >
<ul class="standard-list">
<li ng-repeat="item in items" scroll-if="item.id == first_unread_id">
<span class="content">{{ item.content }}</span>
</li>
</ul>
</div>
JS:
app.directive('scrollIf', function () {
return function (scope, element, attributes) {
setTimeout(function () {
if (scope.$eval(attributes.scrollIf)) {
window.scrollTo(0, element[0].offsetTop - 100)
}
});
}
});
Assuming that the parent element is the one where we scroll, this works for me:
app.directive('scrollIf', function () {
return function(scope, element, attrs) {
scope.$watch(attrs.scrollIf, function(value) {
if (value) {
// Scroll to ad.
var pos = $(element).position().top + $(element).parent().scrollTop();
$(element).parent().animate({
scrollTop : pos
}, 1000);
}
});
}
});
I ended up with the following code (which does not depend on jQ) which also works if the scrolling element is not the window.
app.directive('scrollIf', function () {
var getScrollingParent = function(element) {
element = element.parentElement;
while (element) {
if (element.scrollHeight !== element.clientHeight) {
return element;
}
element = element.parentElement;
}
return null;
};
return function (scope, element, attrs) {
scope.$watch(attrs.scrollIf, function(value) {
if (value) {
var sp = getScrollingParent(element[0]);
var topMargin = parseInt(attrs.scrollMarginTop) || 0;
var bottomMargin = parseInt(attrs.scrollMarginBottom) || 0;
var elemOffset = element[0].offsetTop;
var elemHeight = element[0].clientHeight;
if (elemOffset - topMargin < sp.scrollTop) {
sp.scrollTop = elemOffset - topMargin;
} else if (elemOffset + elemHeight + bottomMargin > sp.scrollTop + sp.clientHeight) {
sp.scrollTop = elemOffset + elemHeight + bottomMargin - sp.clientHeight;
}
}
});
}
});