1

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&nbsp;</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.

Community
  • 1
  • 1
Daaren
  • 36
  • 5

0 Answers0