Search code examples
javascriptangularjsionic-frameworkionic-view

Dynamic value with Ionic scroll-delegate


My situation is that I have made a ion-view which is used by five pages. These pages are navigated through a "tab like" menu, which does not use ion-tabs or anything similar (not really the point of the question). I now would like to use dynamic value for the Ionics scroll-delegate but it does not seem to work. I've read other questions (on SO and Ionic forums) on the same topic but they are old topics from the time of Ionic 1.0.0 or so and the solutions offered doesn't work as of today.

My ion-view template:

<ion-view>
  <ion-content delegate-handle="{{category.id}}" on-scroll="scrollEvent(category.id)">
    <h1>{{category.title}}</h1>
  </ion-content>
</ion-view>

My controllers scrollEvent function:

$scope.scrollEvent = function(category) {
  var scrollamount = $ionicScrollDelegate.$getByHandle(category).getScrollPosition().top;
  if (scrollamount > 180) {
    console.log('scroll amount > 180');
  } else {
    console.log('scroll amount < 180');
  }
};

This gives the following warning in the console:

Delegate for handle "12345" could not find a corresponding element with delegate-handle="12345"! getScrollPosition() was not called! Possible cause: If you are calling getScrollPosition() immediately, and your element with delegate-handle="12345" is a child of your controller, then your element may not be compiled yet. Put a $timeout around your call to getScrollPosition() and try again.

I've tried giving the delegate-handle value on $ionicView.enter $ionicView.afterEnter and $ionicView.loaded. I've given functions timeouts of 10 seconds etc but it doesn't help.

If I set the delegate-handle manually and run it, it works. I would need to set it dynamically because the different views should have their own delegate-handle for me to be able to separate them from each other and set scroll positions etc accordingly. Also the id's of categories might change.

Ionic makes these "panes" (caches them) when navigating through the pages so I can see and check that the handles are correct.

<ion-view nav-view="active" style="opacity: 0.9; transform: translate3d(-33%, 0px, 0px);">
  <ion-content delegate-handle="12345" on-scroll="scrollEvent(category.id)">
    <h1>Category 12345 title</h1>
  </ion-content>
</ion-view>

I would not like to go and alter Ionics code itself.

I've made a simple demo with a similar kind of functions for you to play around with which dublicates the problem: http://codepen.io/thepio/pen/xORdNG

Some might say that obviously I'm getting the data (category etc) on my real application from a http request.


Solution

  • The issue with the delegate-handle directive of ionic is that it actually takes a string as an input. So when you write {{myModel}}, it doesn't interpolate it with the model's value, it takes "{{myModel}}" as the delegate handle identifier. So if we don't want to change anything on ionic's core while waiting for ionic's team to solve this issue, here's my solution to this problem:

    Put the ids on the ion-content tag the same way you are assigning the delegate handle. I.e. <ion-content id="{{category.id}}"></ion-content>

    Then get the actual instance of the handler of current view and use that, it will always be accurate this way (since we're using the id attribute as the identifier using the code below:

        //the idea is to get the specific instance of the handle element using ids, that's what we do in the function below in our controller.
       $scope.getScrollPositionEpicly = function (category) {
            var instances = $ionicScrollDelegate["_instances"];
            //get all the instances that ionic scroll delegate is handling 
            var instance = null;
            for(var index in instances){
              if(instances[index].$element[0].id === category){ //match the instance that we're targeting using the id that we provided , i.e. the current view/handle/id on ion-content
                instance = instances[index];
              }
            }
            if(instance && instance.getScrollPosition){
              return instance.getScrollPosition(); //return the correct position
              console.log(JSON.stringify(instance.getScrollPosition()));
            }
            return {top:0,left:0}; //fallback case/default value so execution doesn't break
       }
      $scope.scrollEvent = function(category) {
        // var scrollamount = $ionicScrollDelegate.$getByHandle(category).getScrollPosition().top;
        var scrollPosition = $scope.getScrollPositionEpicly(category);
        var scrollAmount = (scrollPosition.top || 0);
        console.log("category : " + category);
        console.log("scrollPosition : " + JSON.stringify(scrollPosition));
        if (scrollAmount > 10) {
          console.log('scroll amount > 10');
        } else {
          console.log('scroll amount < 10');
        }
      };
    

    DEMO: Here's the working demo of the code (check console for output)

    Hope this helps.