Benjamin Such Benjamin Such - 1 month ago 5
AngularJS Question

How to get async html attribut

I have a list of items retreived by an async call and the list is shown with the help of

ng-repeat
. Since the div container of that list has a fixed height (400px) I want the scrollbar to be at the bottom. And for doing so I need the
scrollHeight
. But the
scrollHeight
in
postLink
is not the final height but the initial height.

Example

ppChat.tpl.html

<!-- Height of "chatroom" is "400px" -->
<div class="chatroom">
<!-- Height of "messages" after all messages have been loaded is "4468px" -->
<div class="messages" ng-repeat="message in chat.messages">
<chat-message data="message"></chat-message>
</div>
</div>


ppChat.js

// [...]
compile: function(element) {
element.addClass('pp-chat');

return function(scope, element, attrs, PpChatController) {
var messagesDiv;
// My idea was to wait until the messages have been loaded...
PpChatController.messages.$loaded(function() {
// ...and then recompile the messages div container
messagesDiv = $compile(element.children()[0])(scope);
// Unfortunately this doesn't work. "messagesDiv[0].scrollHeight" still has its initial height of "400px"
});
}
}


Can someone explain what I missed here?

As required here is a plunk of it

Answer

You can get the scrollHeight of the div after the DOM is updated by doing it in the following way.

The below directive sets up a watch on the array i.e. a collection, and uses the $timeout service to wait for the DOM to be updated and then it scrolls to the bottom of the div.

chatDirective.$inject = ['$timeout'];

function chatDirective($timeout) {
  return {
    require: 'chat',
    scope: {
      messages: '='
    },
    templateUrl: 'partials/chat.tpl.html',
    bindToController: true,
    controllerAs: 'chat',
    controller: ChatController,
    link: function(scope, element, attrs, ChatController) {
      scope.$watchCollection(function () {
        return scope.chat.messages;
      }, function (newValue, oldValue) {
        if (newValue.length) {
          $timeout(function () {
            var chatBox = document.getElementsByClassName('chat')[0];
            console.log(element.children(), chatBox.scrollHeight);
            chatBox.scrollTop = chatBox.scrollHeight;
          });
        }
      });
    }
  };
}

The updated plunker is here.

Also in your Controller you have written as,

  var Controller = this;

  this.messages = [];

It's better to write in this way, here vm stands for ViewModel

AppController.$inject = ['$timeout'];

function AppController($timeout) {
  var vm = this;

  vm.messages = [];

  $timeout(
    function() {
      for (var i = 0; i < 100; i++) {
        vm.messages.push({
          message: getRandomString(),
          created: new Date()
        });
      }
    },
    3000
  );
}
Comments