ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
51.09k stars 13.51k forks source link

Failed to use delegate-handle for several instances of ion-slide-box #1865

Closed MaximShoustin closed 10 years ago

MaximShoustin commented 10 years ago

I have 3 ion-slide-box. Everyone has 3 items.

I created two buttons Next and back to simulate sliding by pressing them.

However delegate-handle doesn't work. I get:

Delegate for handle "slider1" could not find a corresponding element with delegate-handle="slider1"! next() was not called!

Sounds like delegate-handle doesn't register properly the instance ids.

This is what I have so far:

scope.nextSlider = function(sliderId){
         $timeout(function(){
             $ionicSlideBoxDelegate.$getByHandle(sliderId).next();
      }, 1000);       
      }

       scope.prevSlider = function(sliderId){         
         $timeout(function(){
             $ionicSlideBoxDelegate.$getByHandle(sliderId).previous();
      }, 1000);         
      } 

Template

 <ion-slide-box delegate-handle="{{sliderId}}" 
                       on-slide-changed="slideHasChanged(index)" 
                       show-pager="false">
      <ion-slide ng-repeat="color in colors">        
             <!-- -->
        </ion-slide>
    </ion-slide-box>

And this is a DEMO I play with

The issue was posted in Ionic Forum

mlopez-firstfactory commented 10 years ago

I already posted a solution in the ionic forum, but it isn't in the repo yet, so I'll just copy what I posted back there:

I was having this issue as well, the problem is I am generating the slide boxes dynamically, which means the delegate-handles are supposed to be generated by angular, when I logged the $ionicSlideBoxDelegate, I could see that it had six instances but their delegate-handles weren't being interpolated, they had the angular code as a string: "slider-{{id}}", and I kept getting this error:

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

For this to work, I had to modify the slidebox source code, what I did was to add the delegateHandle attribute in the scope, so this way it is interpolated.

Like this:

scope: { ..., delegateHandle: '@' }

and then I changed this line of code:

var deregisterInstance = $ionicSlideBoxDelegate._registerInstance(slider, $attrs.delegateHandle);

to:

var deregisterInstance = $ionicSlideBoxDelegate._registerInstance(slider, $scope.delegateHandle);

My delegate-handles are now being interpolated and I can also access the handles correctly.

Not sure if this is a "good" solution, hope it helps!

MaximShoustin commented 10 years ago

Good catch. I don't know if its good solution, anyways Ionic team must run regression tests and include these changes as bug fix. Thanks,

mhartington commented 10 years ago

@ajoslin, want to keep this mind with the slide-box rework?

mikesurowiec commented 10 years ago

This is terrible, but for anyone who must use a generated delegate handle, this is a temporary solution where you don't have to hack the source.

In your template:

...
<ion-content delegate-handle="WILLREPLACE" on-scroll="onScroll()">
...

In your corresponding directive:

...
compile: function ($element, attrs) {
   attrs.generatedHandle = Math.random().toString(36).substring(7);
   $element[0].innerHTML = $element[0].innerHTML.replace('WILLREPLACE', attrs.generatedHandle);
   return postLink;
}
...
manu-pandey commented 9 years ago

Hi, When will this fix be available? I upgraded to latest (1.0.0-beta 13) available at ionic website but issue still exits in there.

Nightly fixes this issue but I am seeing some other issues with the nightly.

Thanks

gastonbesada commented 9 years ago

@mhartington Mike, I'm having same issue in 1.0.0-beta.14-nightly-964. Could you please, help to solve this issue?

Thanks!

danfaber commented 9 years ago

I had an issue with this and used this workaround which does not involve modifying the ionic source:

Give each slide a unique id which can be an angular binding. Then you just need to filter on the $ionicScrollDelegate instances collection to get the one which has an element with the id you're looking for on.

Here's a quick copy and paste of my code in case it helps:

 <ion-slide ng-repeat="page in pages" active-slide="page">

            <ion-content id="{{page.name}}">

...

        $scope.getScrollPosition = function (page) {

        var instances = $ionicScrollDelegate["_instances"];

        var instance = _(instances).find(function(ins) {
            return ins.$element[0].id === page.name;
        });

        console.log(JSON.stringify(instance.getScrollPosition()));
    };
panurge-ws commented 9 years ago

An universal directive to use random-generated delegate-handle (inspired to @mikesurowiec ) :

angular.module('app').directive('randomDelegateHandle', [function () {
  return {
    priority: 1000,
    restrict: 'A',
    compile: function compile(tElement, tAttrs, transclude) {
        tAttrs.$set('delegateHandle', Math.random().toString(36).substring(7));
        return function postLink(scope, iElement, iAttrs, controller) {}
    }
};

}]);

use:

ion-content random-delegate-handle
steveoc64 commented 9 years ago

Still appears to be messy using 1.0.0-rc.1

danfaber's comment above was really useful in working out what the problem is, but the find() fn is not totally portable it seems :(

I just created a very simple service to manage jumping between tabs in my application (which has lots of nested tabs, and breaks quickly using ionic's built in tab delegate functions).

A horrible solution perhaps .... but this is working very reliably for me at the moment, and it bypasses any need to touch the underlying ionic libs. The code below is verbose and non-optimal, but its easy to reason about, and portable.

// A fix for broken ionic tabs delegate

.factory('$tabs', ['$ionicTabsDelegate', function($ionicTabsDelegate) {
  return {
    get: function(handle) {

      var tabs = $ionicTabsDelegate._instances
      var theTab = undefined

      // Manually walk ionic's internal array of tabs, and find the matching tab
      // use angular's built in array iterator for best portability
      angular.forEach(tabs, function(v,k){
        if (v.$$delegateHandle === handle) {
          theTab = v
        }
      })
      return theTab
    },
    select: function(handle,position) {
      console.log('tabs select',handle,position)

      var theTab = this.get(handle)
      if (theTab !== undefined) {
        theTab.select(position)
      }
    }
  }
}])

Usage :

<ion-tabs delegate-handle="MainTabs">
 ....   lots of tabs in here
</ion-tabs>

.controller('myExamplecontroller', ['$tabs', function($tabs) {
    return {
       showFirstTab:  function() {
           $tabs.select('MainTabs',0)
       },
       showSecondTab:  function() {
           $tabs.select('MainTabs',1)
       },
      // etc
    }

}])
ringo22 commented 9 years ago

I think it find a possibility for solve this error "Delegate for handle "my-handle" could not find a corresponding element with delegate-handle..."

This possibility is caused why $ionicSideMenuDelegate.$getByHandle('menuLeftHandle')._instances is a Array $ionicSideMenuDelegate.$getByHandle('menuLeftHandle')._instances[n]. anyMethod()

saideldah commented 8 years ago

Hello is this issue has been solved?

i still have same problem with generated delegate-handle

this is my code JS Controller $scope.swapName = "swap00"; $scope.ds = function (sN) { $ionicSlideBoxDelegate.$getByHandle(sN).enableSlide(false); }; HTML `

` ` 1 ` ` 2 ` ` 3 ` `

`

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

thepi0 commented 8 years ago

I had a similar problem and asked it in StackOverflow and got a great temporary solution to the problem. See it at StackOverflow. So basically set and get the delegate-handle by id of the container.

Hopefully this can help someone in the future.

DHclly commented 8 years ago

URL:ionicframework I think we are use in wrong, look at the API , the attr delegate-handle accept value type is string, not expression , but I think the type of string is not suitable.

hope you can understand what I said. ^-^

yacaeh commented 8 years ago

Based on @thepi0 's Link I could solve this problem. For someone who needs help, share code here. You can just put this on your controller. To get dynamically created index of the slide box like with ng-repeat

    $scope.getSlideBoxDelegateHandle = function(delegateIndex) {
        //get all the instances that ionic scroll delegate is handling
        var instances = $ionicSlideBoxDelegate["_instances"];
        //Create Instance you want to get
        var instance = null;
        for (var index in instances) {
            //set DelegateHandle value with your index
            //Ex) instances[index].$$delegateHandle = "slidebox"+index;
            //or just instances[index].$$delegateHandle = index;
            instances[index].$$delegateHandle = index;
            if (index == delegateIndex) {
                instance = instances[index];
            }
        }
            return instance; //return the instance        
    }

usage HTML

`<div class="list" ng-repeat="myIndex in dynamics>

` JS `$scope.slideIndex = $scope.getSlideBoxDelegateHandle(myIndex).currentIndex or anymethod;`
eternalsayed commented 6 years ago

I fixed mine with a custom function with help from @yacaeh 's code above. Basically, instead of comparing indexes, I compared the HTML object of the passed ion-content from the ones available under $ionicScrollDelegate list. Works like charm for me.

function getScrollDelegate(element, $ionicSlideBoxDelegate) {
  var instances = $ionicSlideBoxDelegate["_instances"];
  //Create Instance you want to get
  var instance = null;
  for (var index in instances) {
    //set DelegateHandle value with your index
    //Ex) instances[index].$$delegateHandle = "slidebox"+index;
    //or just instances[index].$$delegateHandle = index;
    instances[index].$$delegateHandle = instances[index].$$delegateHandle || index;
    if (instances[index].$element[0] == element[0]) {
      instance = instances[index];
    }
  }
  return instance; //return the instance
}

And in my directive, I called it like below:

      var $scroll = $('.scroll-content', el.parent());//selector to get the ion-content element in current view
      vm.prevTop = 0; 
      var handle = getScrollDelegate($scroll, $ionicScrollDelegate);//gets the handle
      $scroll.on('scroll', function (e) {
         var pos = handle.getScrollPosition();
         var cl = pos.top>vm.prevTop ?'go-down' :'go-up';
         $(button).removeClass('go-down go-up').addClass(cl);
     })
ionitron-bot[bot] commented 6 years ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.