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
50.94k stars 13.52k forks source link

ion-slides reference broken: this.slides is undefined #11729

Closed Gp2mv3 closed 5 years ago

Gp2mv3 commented 7 years ago

Ionic version: (check one with "x") [ ] 1.x [ ] 2.x [x] 3.x

I'm submitting a ... (check one with "x") [x] bug report [ ] feature request [ ] support request

Current behavior: I receive the error this.slides is undefined following the API documentation: https://ionicframework.com/docs/api/components/slides/Slides/. The reference using @ViewChild(Slides) slides: Slides; doesn't seem to work with Ionic 3.2.1. It was working with Ionic 2 and stopped working after an upgrade to Ionic 3.

Expected behavior: As in Ionic 2, this.slides allows to access the activeIndex with this.slides.getActiveIndex().

Steps to reproduce: Follow the tutorial given bellow: https://ionicframework.com/docs/api/components/slides/Slides/

Related code:

    @ViewChild(Slides) slides: Slides;

...

  public slidesChanged() {
    let index = (this.slides.getActiveIndex();
    console.log(index);
  }
    <ion-slides (ionSlideDidChange)="slidesChanged()" loop="false" pager="true">
...

Other information: Was working like a charm before upgrading to Ionic 3. Is the doc still up to date ?

Ionic info:

Cordova CLI: 6.5.0 
Ionic Framework Version: 3.2.1
Ionic CLI Version: 2.2.2
Ionic App Lib Version: 2.2.1
Ionic App Scripts Version: 1.3.7
ios-deploy version: Not installed
ios-sim version: Not installed
OS: Linux 4.8
Node Version: v7.10.0
Xcode version: Not installed
davec21 commented 7 years ago

Works for me, though I do it slightly different than you.

I do: <ion-slides pager #slides (ionSlideDidChange)="slideChanged()">

then reference it by doing: @ViewChild('slides') slides: Slides;

jgw96 commented 7 years ago

Hello @Gp2mv3 , this is actually a docs issue as the docs are not super clear here. As @davec21 points out, you have to add a template variable to the ion-slides component for ViewChild to know what component to get a reference too. Because of this I am going to mark this as a docs issue.

Gp2mv3 commented 7 years ago

Still doesn't work in fact...

RedEars commented 7 years ago

Same problem here ionic 3.2.0

Related code:

<ion-slides #slider effect="coverflow" slidesPerview="auto" initialSlide="1">
...
</ion-slides>
import { Slides } from 'ionic-angular';
...
@ViewChild('slider') slides: Slides;
...
ionViewDidLoad() {
        this.slides.centeredSlides = true;
}

Error: Cannot set property 'centeredSlides' of undefined

brandyscarney commented 7 years ago

I'm not able to reproduce this using:

<ion-slides #slides (ionSlideWillChange)="onSlideChangeStart($event)" pager>
@ViewChild('slides') slides: Slides;

ionViewDidLoad() {
  console.log("Slides", this.slides);
  this.slides.centeredSlides = true;
}

or

<ion-slides (ionSlideWillChange)="onSlideChangeStart($event)" pager>
@ViewChild(Slides) slides: Slides;

ionViewDidLoad() {
  console.log("Slides", this.slides);
  this.slides.centeredSlides = true;
}

or

<ion-slides (ionSlideDidChange)="slideChanged()" pager>
@ViewChild(Slides) slides: Slides;

slideChanged() {
  let currentIndex = this.slides.getActiveIndex();
  console.log("Current index is", currentIndex);
}

If you are still running into this using the latest version of Ionic 3.3.0 please provide a plunker that reproduces it. Please see this plunker for a starting point: http://plnkr.co/edit/IVp6S8xhHvVt1KNeFZ0o?p=preview

araujobarret commented 7 years ago

Hello Everyone,

I am facing the same issue. My code:

// Template
<ion-content *ngIf="files && uris && isReady">
  <ion-slides #slider loop>
    <ion-slide *ngFor="let file of files; let i = index">   
      <img src="{{src[i]}}"/>
      <p>{{file.description}}</p>
    </ion-slide>
  </ion-slides>
</ion-content>
// ts file
import { ViewChild, Component} from "@angular/core";
import {NavController, NavParams, Slides} from "ionic-angular";
import {SessionService} from "../../app/session.service";
import 'rxjs/Rx';
import {Observable} from "rxjs";

@Component({
  selector: 'page-view-session',
  templateUrl: './view-session.html'
})

export class ViewSessionPage {
  @ViewChild('slider') slides: Slides;  
  files:any = null;
  uris:any = null;
  src:any[] = [];
  isReady: boolean = false;

  constructor(private navCtrl: NavController,
              private navParams: NavParams,
              private sessionService: SessionService){
  }

  ionViewDidEnter() {
    this.files = this.navParams.get("files");
    this.uris =  this.navParams.get("uris");
    let list:any = [];

    console.log(this.files);

    if(this.uris && this.files) {
      for (let i = 0; i < this.uris.length; i++)
        list.push(this.sessionService.getFile(this.uris[i]));

      Observable.forkJoin(list)
        .subscribe((data: any) => {
          for (let i = 0; i < data.length; i++) {
            this.src[i] = data[i].url;
          }
          this.isReady = true;
          console.log(this.slides); // Works fine got the object
          console.log(this.slides.getActiveIndex()); // Returns undefined, and other methods returns undefined too

        });
    }
  }
}

My Ionic info: Your system information:

Cordova CLI: 6.4.0 Ionic Framework Version: 3.2.1 Ionic CLI Version: 2.2.1 Ionic App Lib Version: 2.2.0 Ionic App Scripts Version: 1.3.7 ios-deploy version: Not installed ios-sim version: Not installed OS: Linux 4.6 Node Version: v7.2.0 Xcode version: Not installed

brandyscarney commented 7 years ago

Please try updating to the latest version of Ionic (now 3.4.2): https://github.com/ionic-team/ionic/blob/master/CHANGELOG.md#340-2017-06-15

As I said in https://github.com/ionic-team/ionic/issues/11729#issuecomment-306871806, I am not able to reproduce this. If you can provide a plunker using the latest version we can look into it. I've provided a working example of this issue in this plunker: http://plnkr.co/edit/IVp6S8xhHvVt1KNeFZ0o?p=preview

araujobarret commented 7 years ago

Hello Brandy,

I am not able to create my example in plunker because I am using observables to retrieve data from my server. The main issue is about the lifecycle, I put a div before each slide with a ngIf to only show when the data finish of loading, but putting the div before the slides, it is only showing the image in the slide 0, the others slides still there, I can see the image resource in the debug, but in the app it not show any image. Something like this:

<ion-content *ngIf="files && uris">
  <ion-slides #slider loop>
    <div *ngIf="isReady">
      <ion-slide *ngFor="let file of files; let i = index">   
        <img src="{{src[i]}}"/>
        <p>{{file.description}}</p>
      </ion-slide>
    </div>
  </ion-slides>
</ion-content>

With this I am able to access the methods of the slide variable, but only the first slide render the content, the content still in the debug, but not render on the screen.

Thanks.

araujobarret commented 7 years ago

Hello again guys.

Maybe I have found the main issue working with Ionic Slides, here my plunker that I demonstrate this issue, when we use async function to access an ionic slide component. When we access in the ionViewDidEnter everything goes fine, but if in this part of code we put somethins async, like a simple setTimeout native js function, when we try to access the slide object methods and properties returns undefined.

Here is my plunker example: http://plnkr.co/edit/jxSOftgZgTxYnGYIoj7W?p=preview

PS.: Sorry about my plunker example, I never used it before.

Thanks.

martinhanke commented 7 years ago

Hi @brandyscarney ,

I used your plunkr to reproduce the issue by just adding the following line console.log(this.slides.length()); Link to plunkr: http://plnkr.co/edit/vmNVs28bOXmtie8ifXty?p=preview

L96Github commented 7 years ago

Hi all,

I had a similar problem, this.slides was undefined and for some reason a timeout of 500 milliseconds worked for me.

<ion-slides #slides pager>...
 @ViewChild('slides') slides: Slides;
    ionViewDidLoad() {
        setTimeout(() => {
            this.slides.lockSwipes(true);
        }, 500);      
    }

Hope it helps.

Artfloriani commented 7 years ago

I'm having the same issue and it seems to be related that the Slides component is initialized in one step but its children are not set in the same step.

So if you try to use the length() or slideTo() just after the Slides is initialized it might throw this error. As @L96Github mentioned, a 500ms delay is a workaround for the issue but it is not an ideal way and might not work on every case.

tyftler commented 7 years ago

I had the same issue and the timeout workaround worked for me too. But it turned out it was caused by an *ngIf="" on the slides component (which still worked in earlier Ionic versions).

<ion-slides #slides *ngIf="loaded">
  <ion-slide></ion-slide>
</ion-slides>

After removing it it worked without the timeout.

barakbd commented 7 years ago

Works for me based on @davec21 answer. Docs should be updated. Ionic 3.6.0

mostafa7240 commented 7 years ago

Hey I noticed some mistakes:

1- Please use : @ViewChild('slides') slides: Slides; instead of : @ViewChild('slider') slides: Slides;

2- #slides is required in : <ion-slides #slides (ionSlideWillChange)="onSlideChangeStart($event)" pager>

3- Use ionViewDidLoadinstead of ngAfterViewInit

james11a commented 7 years ago

I am facing the same problem, using latest ionic v3.6.1 In template file, I have:

<ion-slides #slides pager (ionSlideReachStart)="showActiveIndexValue()">

In component.ts file:

@ViewChild('slides') slides: Slides;
public showActiveIndexValue(): void {
    console.log(`Initial slide index: ${this.slides.getActiveIndex()}`);
}

Result: Initial slide index: undefined

Somehow, I can also see that it works if this same function is called using a button click event from within the slide.

mostafa7240 commented 6 years ago

You should care of initializing order. I mean you should not log such thing in the constructor. Use ionViewDidLoad like this:

ionViewDidLoad(){
    console.log(`Initial slide index: ${this.slides.getActiveIndex()}`);
}
deepakkaushik2016 commented 6 years ago

this.slides is null in constructor but it is available in other lifecycle hooks like ionViewWillEnter or ionViewDidLoad etc.

rsc1975 commented 6 years ago

I had the same problem, In my case, the problem was because the <ion-slides> tag is located inside a container with a *ngIf condition that was false until the data arrives, so in the same event handler, after the data array is set, the Slides component still has not been redered, so its reference this.slides is undefined.

IMHO, this is not a ionic issue, actually It's not a bug but an angular feature.

I was getting the Error: Cannot set property 'initalSlide' of undefined. The solution I applied was change the data order, moving the "active" element to the first Slide, but I'm not sure what would be the proper solution if you need to call the Slides API, using a timeout doesn't seem the most beautiful approach, It would be nice to know a better one.

Artfloriani commented 6 years ago

If you have any condition wrapping your <ion-slides> such as *ngIf I would suggest adding a setter for the ion-slides, this way it is independent of the lifecycle hooks and the slides reference is always valid:

    slidesRef: Slides;
    @ViewChild('slides') set content(ionSlides: Slides) {
        if (ionSlides){
            this.slidesRef = ionSlides;
            //Here you can set any properties you would like
            this.slidesRef.autoHeight = true;
        }
    }
hesampour commented 6 years ago

@Artfloriani method not worked correctly. I want to set the slide direction to rtl. and I want to use *ngif. and my component is a separate component and I can not use ionViewDidLoad because its not a page. @Artfloriani has not any error but it seems nothing changes. @ViewChild('slides') set content(ionSlides: Slides) { if (ionSlides){ this.slidesRef = ionSlides; //Here you can set any properties you would like this.slidesRef._rtl = true; } }

andy008 commented 6 years ago

Thanks @Artfloriani worked for me perfectly. I had *ngIf on three sliders.

ionitron-bot[bot] commented 5 years ago

This issue has been automatically identified as an Ionic 3 issue. We recently moved Ionic 3 to its own repository. I am moving this issue to the repository for Ionic 3. Please track this issue over there.

If I've made a mistake, and if this issue is still relevant to Ionic 4, please let the Ionic Framework team know!

Thank you for using Ionic!

ionitron-bot[bot] commented 5 years ago

Issue moved to: https://github.com/ionic-team/ionic-v3/issues/222