Extend ionContent at top

In my message app, a conversation is initially loaded with its newest answers.
It is then possible to load older answers by a button. The new messages are then concatenated at the beginning of the messages array:

[just loaded messages, initially loaded messages]

I want to realize the same effect as in WhatsApp: when older messages are loaded (‘Load More’ button) the content extends at the top, making it possible to scroll to the newly loaded messages. But it should not scroll to top by itself.
How is it possible to realize that with ionic? My current problem is that when new messages are loaded, the content is already scrolled to top and remains to be scrolled to top, of course! And is not at a scroll position somewhere in the middle.
Any help is appreciated!

Take a look at the ion-refresher: http://ionicframework.com/docs/api/directive/ionRefresher/

No, this not what I mean.
I don’t want to trigger the load by pulling, but rather click a button and the content area extends to the top, but the scroll position remains at the old position (which has new coordinates after extending of course).

Then I think calculating the scroll position would be your best bet. Click a button, remember position, append the data which triggers angular to update the ion-list, calculate new position.

(pseudo)code:

$scope.loadMore = function (){
    
    var bottomPosition = calculateScrollAreaHeight() - $ionicScrollDelegate.getScrollPosition().top;
    var extraContent = MyFactory.loadMore(); // order by date ascending
    $scope.myItems = extraContent.concat($scope.myItems); // merge arrays, append current items after extra items

    var newHeight = calculateNewScrollAreaHeight();

    $ionicScrollDelegate.scrollTo(0, newHeight - bottomPosition, false);
}
1 Like

Thank you! This helped me very much!
I ended up with the following code:

$scope.loadMore = function (){

    var bottomPosition = bottomPosition = $ionicScrollDelegate.getScrollMax().top 
               - $ionicScrollDelegate.getScrollPosition().top;
    var extraContent = MyFactory.loadMore(); // order by date ascending
    $scope.myItems = extraContent.concat($scope.myItems); // merge arrays, append current items after extra items

    $ionicScrollDelegate.resize();
    $timeout(function(){
        var newHeight = $ionicScrollDelegate.getScrollMax().top;
        $ionicScrollDelegate.scrollTo(0, newHeight - bottomPosition, false);
    });
}

$ionicScrollDelegate.getScrollMax() is not yet available in ionic, but I created a pull request, and I hope it gets merged.
You might want to specify the ionContent/ionScroll that you want to address by a handle:

$ionicScrollDelegate.$getByHandle('handle').getScrollMax()

I’m trying to do the same thing here. I have a chat window that scrolls up. My edge case can have over 300 messages in a conversation which performs poorly, so I want to start with 10 and then when the user scroll gets near the top add 10 more to the ng-repeat array that is populating the DOM.

I have this working very well, except that adding items results in a visible flicker on the screen after the DOM is updated and before i return the screen to the original position. Eliminating that flicker would make this perfect! I’m hoping someone here has an idea on how to eliminate this flicker.

Here’s the code I’m using:

View HTML

<ion-content id="convScroll" on-scroll="gotScrolled(this)" class="has-subheader has-footer left-menu-content" style="background-color: white;">

  <div style="height: 20px;"></div>
  <div class="bubbleWrapper" ng-repeat="message in displayedConversationMessages" ng-init="vInfo.markedMessages[message.messageKey]=false" keep-scroll>
    <label class="checkbox" style="float: left; width: 50px;" ng-if="vInfo.screenMode==='delete'">
      <input type="checkbox" ng-model="vInfo.markedMessages[message.messageKey]" ng-click="checkAllMarks()">
    </label>
    <div class="bubble" ng-class="[message.direction === 1 ? 'msgFromBubble' : 'msgToBubble',(message.direction === 1 && vInfo.screenMode==='delete') ? 'msgDelMargin' : '']" ng-click="setState('messageDisplay/:messageUUID',{messageUUID: message.messageKey});">
      <span style="white-space: pre-line;">{{message.messagePreview}}</span>
      <div ng-if="message.isForm" class="msgMore"><i class="icon ion-document-text"></i> more <i class="icon ion-chevron-right msgIconRight"></i></div>
    </div>
    <div class="msgBubbleTime" ng-class="[message.direction === 1 ? 'msgFromTime' : 'msgToTime',(message.direction === 1 && vInfo.screenMode==='delete') ? 'msgDelMargin' : '']">{{message.chatTime}}</div>
  </div>
  <div style="height: 20px;"></div>

</ion-content>

Controller Javascript

    $scope.checkScroll = function () {
      console.log('%cScroll stopped', 'color: #acacac;');

      if (_.get($scope, 'infScroll.scrollY') < 300) {
        _.set($scope, 'infScroll.oldScrollHeight', document.querySelector('#convScroll').scrollHeight);
        _.set($scope, 'infScroll.oldScrollY', $ionicScrollDelegate.getScrollPosition().top);

        // Move the message pointer up & add messages to the list
        var oldTopMsgIndex = $scope.infScroll.topMsgIndex;
        $scope.infScroll.topMsgIndex = ($scope.infScroll.topMsgIndex - 10) < 0 ? 0 : $scope.infScroll.topMsgIndex - 10;
        if ($scope.infScroll.topMsgIndex !== oldTopMsgIndex) {
          $scope.displayedConversationMessages = _.slice($scope.allConversationMessages, $scope.infScroll.topMsgIndex, oldTopMsgIndex).concat($scope.displayedConversationMessages);
        }

        // As soon as the DOM is done rendering we will know the new scrolll height and can reset the scroll position
        $timeout(function () {
          var newScrollPosition = (document.querySelector('#convScroll').scrollHeight - $scope.infScroll.oldScrollHeight) + $scope.infScroll.oldScrollY;
          $ionicScrollDelegate.scrollTo(0, newScrollPosition, false);
        }, 0);
        $scope.$apply();
      }

      return true;
    };

    $scope.gotScrolled = function (t) {
      _.set($scope, 'infScroll.scrollY', $ionicScrollDelegate.getScrollPosition().top);
      $scope.$apply();
      if ($scope.scrollCheck) {
        $timeout.cancel($scope.scrollCheck);
        $scope.scrollCheck = null;
      }
      // Wait until no scrolling happens for 1/4 second before doing anything
      $scope.scrollCheck = $timeout($scope.checkScroll, 250);
    };