ionic-framework: bug: ion-slides swiper instance does not get re-initialized with angular ivy

Bug Report

Ionic version:

[x] 5.0.0-rc.2

Current behavior: With angular ivy enabled (9.0.0-rc.12) ion-slides (specifically the swiper instance) does not get re-initialized after destroyed once and is therefore unusable afterwards.

Expected behavior: Swiper instance gets re-initialized, ion-slides keep working.

Steps to reproduce:

  • clone sample app
  • npm i
  • ionic serve
  • open console
  • click on “Go to slides page”
  • click “Go to next slide” (working!)
  • click back button
  • click on “Go to slides page” again
  • “Go to next slide”, “Go to previous slide” is broken
  • check console, unfold logged Swiper object -> destroyed: true

Related code:

A sample application via GitHub

Other information: Removing the following line fixes the problem, but I don’t think that its a solution to the problem.

https://github.com/ionic-team/ionic/blob/ae4e28969f1d7b3e41f16f4f70c6befdaca6118f/core/src/components/slides/slides.tsx#L153

Related: ngx-swiper-wrapper#226 As ionic is shipping the swiper directly embedded into the framework maybe it needs to be compiled with ngcc once.

Ionic info:

Ionic:

   Ionic CLI                     : 5.4.15 (/usr/local/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 5.0.0-rc.2
   @angular-devkit/build-angular : 0.900.0-rc.12
   @angular-devkit/schematics    : 9.0.0-rc.12
   @angular/cli                  : 9.0.0-rc.12
   @ionic/angular-toolkit        : 2.1.2

Cordova:

   Cordova CLI       : 9.0.0 (cordova-lib@9.0.1)
   Cordova Platforms : ios 5.1.1
   Cordova Plugins   : cordova-plugin-ionic-keyboard 2.2.0, cordova-plugin-ionic-webview 4.1.3, (and 4 other plugins)

Utility:

   cordova-res : not installed
   native-run  : not installed

System:

   ios-deploy : 1.9.2
   ios-sim    : 8.0.2
   NodeJS     : v12.4.0 (/usr/local/bin/node)
   npm        : 6.11.3
   OS         : macOS Catalina
   Xcode      : Xcode 11.3.1 Build version 11C504

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 18
  • Comments: 38 (18 by maintainers)

Commits related to this issue

Most upvoted comments

Here is a workaround for this issue. Simply postpone the slider initialization with ngIf and the angular lifecycle.

my-component.html

<ion-slides *ngIf="didInit">
   <!-- multiple <ion-slide/> -->
</ion-slides>

my-component.ts

@Component({
    templateUrl: 'my-component.html'
})
export class MyComponent implements AfterViewInit {
    public didInit: boolean = false;

    ngAfterViewInit() {
        this.didInit = true;
    }
}

Thanks for the issue. This has been resolved via https://github.com/ionic-team/ionic/pull/20899 and will be available in an upcoming release of Ionic Framework.

Hi everyone,

We are still investigating how to properly fix this. I will post more here when I have another update. In the meantime feel free to use any of the workarounds provided in this thread.

Thanks!

No, the PR I added prevents this performance issue from happening.

@abuassar Can you try this dev build and let me know if it fixes the duplicate events?

npm i @ionic/angular@5.1.0-dev.202003272115.6f84a4f

@liamdebeasi did you find out why the slider is initialized (or connected) multiple times?

To summarize, Angular Ivy creates new pages as siblings of their router outlets, but Ionic Framework requires them to be children of the router outlets.

What Angular Ivy wants:

<ion-router-outlet></ion-router-outlet>
<my-page-with-slides></my-page-with-slides>

What Ionic Framework wants:

<ion-router-outlet>
  <my-page-with-slides></my-page-with-slides>
</ion-router-outlet>

To work around this, we call ionRouterOutlet.appendChild(myPageWithSlides) which is going to “move” the page from being a sibling to being a child of the router outlet.

When Web Components are attached to the DOM, the connectedCallback method is called, and when Web Components are removed from the DOM, the disconnectedCallback method is called. So moving a component is going to cause connectedCallback to be called, followed by disconnectedCallback, followed by another connectedCallback.

The problem with ion-slides is that the disconnectedCallback method was asynchronous, meaning it was possible for the Swiper instance to be destroyed after the second connectedCallback, resulting in ion-slides being left in a destroyed state.

i’ll try this evening if everything works when we can expect the prod release? i’d like to not have dev branches on production environment

We do not typically comment on exact timing of releases, but we are in a regular release cycle with new releases every few weeks.

I can also confirm this problem 😃 . Hopefully there will be a fix soon 🙏

To work around this, we call ionRouterOutlet.appendChild(myPageWithSlides) which is going to “move” the page from being a sibling to being a child of the router outlet.

@liamdebeasi will this be a performance issue? Because swiper than is always initialized twice in angular.

@liamdebeasi The dev-build fixes the issue for me too, thank you!

I was desperate trying to figure out what was going on with an <ion-slide> implementation. I need to navigate to a slide index after clicking on an ion-segment.

Sometimes, (90% of the time) the implementation does not work. The slideTo method did not resolve and nothing happens. I tested the version (npm i @ionic/angular@5.1.0-dev.202003271919.8a161f0) and the problem seems to have been solved.

Can everyone try the following dev build and let me know if it resolves the issue? I tested in the repo provided in #20356 (comment), and everything seems to work.

npm i @ionic/angular@5.1.0-dev.202003271919.8a161f0

Thanks!

Hi @liamdebeasi i’ve installed the package that you’ve gave us, it’s working fine for my case too, also on production build.

I don’t see many breaking changes for my Ionic usage, so i’ll publish an update with this version waiting for the next production one.

Thank you for your work

@liamdebeasi very nice, I tried it and there are no more duplicated init yay!

@liamdebeasi did you find out why the slider is initialized (or connected) multiple times?

To summarize, Angular Ivy creates new pages as siblings of their router outlets, but Ionic Framework requires them to be children of the router outlets.

What Angular Ivy wants:

<ion-router-outlet></ion-router-outlet>
<my-page-with-slides></my-page-with-slides>

What Ionic Framework wants:

<ion-router-outlet>
  <my-page-with-slides></my-page-with-slides>
</ion-router-outlet>

To work around this, we call ionRouterOutlet.appendChild(myPageWithSlides) which is going to “move” the page from being a sibling to being a child of the router outlet.

When Web Components are attached to the DOM, the connectedCallback method is called, and when Web Components are removed from the DOM, the disconnectedCallback method is called. So moving a component is going to cause connectedCallback to be called, followed by disconnectedCallback, followed by another connectedCallback.

The problem with ion-slides is that the disconnectedCallback method was asynchronous, meaning it was possible for the Swiper instance to be destroyed after the second connectedCallback, resulting in ion-slides being left in a destroyed state.

i’ll try this evening if everything works when we can expect the prod release? i’d like to not have dev branches on production environment

We do not typically comment on exact timing of releases, but we are in a regular release cycle with new releases every few weeks.

still good enough for me if it solve my use case, thank you 😁

Can everyone try the following dev build and let me know if it resolves the issue? I tested in the repo provided in https://github.com/ionic-team/ionic/issues/20356#issue-558204094, and everything seems to work.

npm i @ionic/angular@5.1.0-dev.202003271919.8a161f0

Thanks!

Hello everyone. I can confirm the same behaviour on the latest ionic and angular versions.

so, what is the temp walkaround for this issue now?

Use Angular 8, or if you’ve already upgraded to Angular 9, disable Ivy. https://angular.io/guide/ivy

Looks like there was a breaking change in Angular 9 that we missed 😄. I will mark this as a bug and provide an update here when I have more to share.

Thanks for the issue. I’ve been digging into this issue, and this behavior appears to be a bug in Angular 9. We are reaching out to the Angular team and will post an update here when we have it.

In the meantime, we recommend sticking with Angular 8.