I am a newbie in AngularJS and web development in general, so please excuse any naïve comment from me.
In a small project I am working on, there is a page with a gallery to preview a set of items. Each item contains a table which lists several rows. As the items should be all equal, the tables have fixed sizes. In case not all of the rows can fit the preview, I want to show the user a clue that there is more to read.
A the beginning this seemed a simple thing to me, but it turned out to be a tricky thing to do. I thought to check for scrollHeight
and offsetHeight
properties of the table, but as it is generated via ng-repeat
, AngularJS played me fool.
I put my final solution in this Plunker, but it seems too complicated to me, and I am asking if anyone has a simpler solution.
After a lot of Googling and trials, I created a custom directive for the table, which registers an handler for a custom event:
<div class="col-xs-4" ng-repeat="item in items" style="position: relative;">
<table check-overflow="isOverflow">
<tr ng-repeat="row in [1,2,3,4,5,6,7,8,9,10] | limitTo: item.rows">
<td>Qty </td>
<td>Description{{$odd ? ', and an even longer description' : ''}}</td>
</tr>
</table>
<div class="more-indicator" ng-show="isOverflow">
<span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span>
</div>
</div>
In the directive I set a scope variable which drives the ng-show
according to the actual size of the table:
angular.module('tableOverflow').directive('checkOverflow',[ function() {
return function(scope, elm, attr) {
// when the rendering of the gallery is completed, set the overflow
// marker to show or stay hidden
scope.$on('galleryCompleted', function (event, data) {
scope[attr.checkOverflow] = (elm[0].scrollHeight > elm[0].offsetHeight);
});
};
}]);
The tricky thing was to find out the right moment to fire the event: I wasn't able to do it in the table's scope (i.e.: when the $last
row is available), but have to put a fake div outside the outer loop and invoke a scope function to do it:
<div ng-init="OnCompleted()"></div>
and this in the controller:
$scope.OnCompleted = function() {
$timeout(function() {
$scope.$broadcast('galleryCompleted');
}, 200); // need some timeout or won't work always on a fast browser
};
Anyone can suggest a simpler solution? Thank you very much in advance!
EDIT
I don't like the $timeout
thing, also: the behavior can change across devices, PCs, etc. Thks!
UPDATE
Googling a little further, I found a suggestion, in another post, that gave me some hope, via the $watch
:
angular.module('tableOverflow').directive('checkOverflow',[ function() {
return function(scope, elm, attr) {
var isCompleted = function() {
scope[attr.checkOverflow] = (elm[0].scrollHeight > elm[0].offsetHeight);
console.log('isCompleted ' + elm[0].scrollHeight + '; ' + elm[0].offsetHeight);
};
var watch = scope.$watch(function() {
console.log('watch: ' + elm[0].scrollHeight);
return elm[0].scrollHeight;
}, function() {
scope.$evalAsync(isCompleted);
});
};
}]);
However, it doesn't works; or better, not always. Reloading the page sometimes the scrollHeight
change as expected while the DOM is rendering, but the $watch
is not firing. Any clue?
FINAL UPDATE
I updated the Plunker to compare all the approaches: $last
, fake div and $watch
. It's definitely weird, the $watch
seems always working in the Plunker, but not on my local PC; works always in IE 11, but not in Chrome.
I guess this is some internal behavior of Angular I am trying to catch from outside. Any comment will be welcomed.