Ionic-framework: Slides.slideTo not working when filling slides with data of promise

Created on 31 May 2016  路  35Comments  路  Source: ionic-team/ionic-framework

Short description of the problem:

After filling the slides with data of an promise, sliding to specific slide is not working.
When I click on the button, then the sliding to specific slide is working. But I want to slide the slider to an specific slide by startup of the page.
See plunker for example of issue. (home.html & home.ts)

What behavior are you expecting?

Relevant code:

  @ViewChild('mySlider') slider:Slides;
  private slides:JSON;

  constructor() {
        this.getSlidesOfApi().then((slides:JSON) => {
          this.slides = slides;
          this.slider.slideTo(3,0);
        });
    }


    private getSlidesOfApi():Promise<JSON>{
      return new Promise((fulfill, reject) => {
        let jsonSlides = [
          {title:"slide 1"},{title:"slide 2"},{title:"slide 3"},{title:"slide 4"},{title:"slide 5"}
        ];
        fulfill(jsonSlides);
      })
    }

    public slide(){
      this.slider.slideTo(3,0);
    }

Which Ionic Version? 2.x

Plunker that shows an example of your issue

http://plnkr.co/edit/LTGjnmJMTjqwX25xtFjV?p=preview

Run ionic info from terminal/cmd prompt: (paste output below)
Your system information:

Cordova CLI: 5.4.1
Gulp version: CLI version 3.9.0
Gulp local: Local version 3.9.1
Ionic Framework Version: 2.0.0-beta.7
Ionic CLI Version: 2.0.0-beta.25
Ionic App Lib Version: 2.0.0-beta.15
ios-deploy version: Not installed
ios-sim version: Not installed
OS: Mac OS X El Capitan
Node Version: v5.3.0
Xcode version: Xcode 7.3.1 Build version 7D1014

v3

Most helpful comment

@brandyscarney I see why this is failing. Can I propose alternative implementation to initialisation of Slides?
Now we have this https://github.com/driftyco/ionic/blob/master/src/components/slides/slides.ts#L396 :

setTimeout(() => {
   var swiper = new Swiper(this.getNativeElement().children[0], options);
   this.slider = swiper;
}, 300);

So we actually make it initialise later for whatever good reason is for that.

What I propose is change that to make it observable:

class Slides{
  /**
   * private
   */
  slider$: BehaviorSubject<Swiper> = new BehaviorSubject(null);

  /**
   * public for dealing with async initialisation of Swiper;
   */
  getSlider$(): Observable<Swiper>{
    return this.slider$;
  }

  ngOnInit() {
    [...]
    Observable.of(options)
      .debounceTime(300)
      .map((options)=>new Swiper(this.getNativeElement().children[0], options))
      .subscribe((swiper)=>{
        this.slider = swiper;
        this.slider$.next(swiper);
      });
  }

}

This implementation solve CORE issue which is async initialisation of slider. It makes also easy for other developers to catch Swiper object after it's initialised.

All 35 comments

@Ionitron It is on both the platforms: android and ios

There are console errors of Cannot read property 'slideTo' of undefined. The ViewChild is set before the ngAfterViewInit callback is called so this needs to be in the ngAfterViewInit callback. Then, there is something going on with the timing that we'll have to look into. This workaround is working for me:

ngAfterViewInit() {
  this.getSlidesOfApi().then((slides:JSON) => {
    this.slides = slides;
    setTimeout(() => {
      this.slider.slideTo(3,0);
    });
  });
}

Let me know if that helps!

@brandyscarney It still doesn't work if you do it with an Http request.
I updated the plunk: http://plnkr.co/edit/LTGjnmJMTjqwX25xtFjV?p=preview

@brandyscarney if you set the timing on the settimeout on 150 then it will work in the plunker, but not when you run it on a phone. So messing with the timeout seems not a good solution.

@brandyscarney any updates on this?

@jgw96 Any updates on this? I need this..

Any updates on this?

I still got the issue..

Can anyone please provide a plunker that reproduces the problem? The plunker attached is working for me.

@brandyscarney It is working on the plunker, but not on a phone.

@elineopsommer Ahh yeah sorry. Unfortunately I haven't had time to really look into this, but I believe it is caused by the init of the slider being wrapped in a setTimeout. There are some timing errors in slider and I believe it will require a rewrite of the component: https://github.com/driftyco/ionic/blob/master/src/components/slides/slides.ts#L396

Do you have a timeline on it?

@brandyscarney I see why this is failing. Can I propose alternative implementation to initialisation of Slides?
Now we have this https://github.com/driftyco/ionic/blob/master/src/components/slides/slides.ts#L396 :

setTimeout(() => {
   var swiper = new Swiper(this.getNativeElement().children[0], options);
   this.slider = swiper;
}, 300);

So we actually make it initialise later for whatever good reason is for that.

What I propose is change that to make it observable:

class Slides{
  /**
   * private
   */
  slider$: BehaviorSubject<Swiper> = new BehaviorSubject(null);

  /**
   * public for dealing with async initialisation of Swiper;
   */
  getSlider$(): Observable<Swiper>{
    return this.slider$;
  }

  ngOnInit() {
    [...]
    Observable.of(options)
      .debounceTime(300)
      .map((options)=>new Swiper(this.getNativeElement().children[0], options))
      .subscribe((swiper)=>{
        this.slider = swiper;
        this.slider$.next(swiper);
      });
  }

}

This implementation solve CORE issue which is async initialisation of slider. It makes also easy for other developers to catch Swiper object after it's initialised.

@brandyscarney as #7212 is done will you be so kind to look on my proposal above for this issue?

My turn ;-) any update on this?

I have the same problem - no problem when running in the browser, but on the phone I get ERROR: EXCEPTION: Uncaught (in promise): TypeError: undefined is not an object (evaluating 'this.slider.slideTo'). Using Ionic RC0.

any news here? Have the same issue and no good solution for this yet.

This may not be the best solution to this problem, but it works out pretty good for a temporary workaround and hopefully helps someone until we get a more permanent solution.

@ViewChild('weekSlider') weekSlider: Slides;

private _watchSlideLength():any {
        if(!this.weekSlider || this.weekSlider.length() < 1) {
            console.log('not ready');
            return setTimeout(() => {
                return this._watchSlideLength();
            }, 50);
        }
        else {
            console.log('ready', this.weekSlider.length());
            return setTimeout(() => {
                this.weekSlider.slideTo(2);
            }, 50);
        }
}

In my case, the data I'm loading is 5 weeks worth of data and so I want to slide to the middle of the five weeks that get loaded after the data gets loaded. I call this function after I get my data and it watches for the slider to be initialized and have a length of greater than 0 to know that the slider is ready. Once it has a length, the slideTo method should be available.

Like I mentioned above, it's not a great solution, but it's one that has been working for me (Android & iOS). Hope it helps someone! :)

@dallastjames is there a way to implement that with an observable?

@slzr When I originally wrote that I couldn't find any accessible observables regarding the slider to subscribe to that would help know when the slider had loaded the slides based on the data. I know that the latest release of Ionic RC5 the slides component was refactored and rewritten and so it may have better compatibility at this point. I haven't looked at the changes much to see if there is anything new that could help refactor the above solution to use observables, but there may be. It may be worth looking at some of the new output events to see if any fire as the slides are being dynamically created.

i try to add
import { Slides } from 'ionic-angular';
and it works.
i hope it can help you.

I'm assuming this still hasn't really gone anywhere?

it work in my code
place the slideTo in ionViewDidEnter function
ionViewDidEnter() { this.slides.slideTo(this.itemIndex, 500); }

check this. lifecycle view in ionic http://blog.ionic.io/navigating-lifecycle-events/

Still an issue, and not just on iOS

I was playing with various output events and couldn't find any firing at the appropriate moment. The workaround proposed by @dallastjames is still the best one available now.

yup, just wait until the slider length is what you would expect "then" call slider.slideTo... but there really, really, really should be some form of promise on the slider when it has updated the contained slides so you can chain a then(()=>{}).

Maybe something like slider.onSlidesChanged() ???

For the same scenario (slides based on data from API call), i'm able to get this slideTo works only when the slide changes are watched through _ViewChildren_ (QueryList) as below :

// Mentioned only the lines required to keep it simple
@ViewChild(Slides) slides: Slides;
@ViewChildren(Slide) slideCollection: QueryList<Slide>;

slideIndexToMove = 2;

// Ionic lifecycle
ionViewDidLoad() {

 this.slideCollection.changes.subscribe((r) => {
    setTimeout(() => {
       this.slides.slideTo(this.slideIndexToMove, 0);
    });
 });
}

This issue is still a thing in ionic 3 馃憥

Yup, quite a few people complaining about really long terms bugs in ionic of late, but they have been busy with ionic 4, so....

As posted above, the only solution I found was using a wait loop to check for the length of slides you are expecting.... or just delaying the execution path once for an arbitrary period of time (say... 500ms) as some have suggested.

Problem still exists on latest ionic.
Was debugging over and over until I found this post.
@dallastjames solution saved my day!

I've been grappling with this issue for a while. I have a set of sliders that are being shown and hidden by an ngIf. Without a timeout long enough the slides consistently return a length of undefined, which of course wrecks anything you're trying to in addition to showing the slides, such as moving to a specific slide. So for me the solution of waiting for the length on the slides was not working.

I did discover that i could use the (ionSlideReachStart) on the ion-slides element as a safe indicator that the slides were ready. I use this event as a staring point. In my case I have a set Timeout loop checking a boolean that is set to true by the ionSlideReachStart. Obviously there's more happening when hiding the slider etc, but for me this was a good place to start.

I realize that for anyone using this event for other functionality on the slides might run into some other issues, but hopefully someone might find this solution useful. Still a hack, so I hope a more specific envent might be available in the future.

I got it working with the solution by @baihaqyaviq - thanks so much!

Solution at https://github.com/ionic-team/ionic/issues/6703#issuecomment-292721464

Thanks for the issue! We have moved the source code and issues for Ionic 3 into a separate repository. I am moving this issue to the repository for Ionic 3. Please track this issue over there.

Thank you for using Ionic!

Thanks for the issue! We have moved the source code and issues for Ionic 3 into a separate repository. I am moving this issue to the repository for Ionic 3. Please track this issue over there.

Thank you for using Ionic!

Was this page helpful?
0 / 5 - 0 ratings