angularserver-side-renderingswiper.jsangular17angular17-ssr

How to initialize main & thumb Swiper in Angular when init is false - Swiper 11.0.6 Angular 17 SSR


I am utilizing Swiper in several of my components, and I've encountered an issue when Angular routing changes especially routeParams eg. /route/:id – it doesn't function correctly. To address this, I implemented ngZone. This resolved the main Swiper functionality, but the thumbs Swiper is still behaving unexpectedly. I believe that rather than initializing twice, it would be more efficient to initialize once with both the main Swiper and thumb Swiper details. However, I am unsure about the syntax. Can someone please assist me with this?

Note: The objective is to ensure that Swiper works seamlessly when navigating between different routes.

html:

    <div>
    <swiper-container #swiper1 style="--swiper-navigation-color: #fff; --swiper-pagination-color: #fff" class="mySwiper"
        thumbs-swiper=".mySwiper2" navigation="true" init="false">
        <swiper-slide *ngFor="let slide of slides">
            <img [src]="slide.image" />
        </swiper-slide>
    </swiper-container>

    <swiper-container #swiper2 class="mySwiper2" space-between="10" slides-per-view="6" free-mode="true"
        watch-slides-progress="true" init="false">
        <swiper-slide *ngFor="let slide of slides">
            <img [src]="slide.image" />
        </swiper-slide>
    </swiper-container>
</div>

css:

swiper-slide {
  text-align: center;
  font-size: 18px;
  background: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
  background-size: cover;
  background-position: center;
  img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

.mySwiper {
  height: 600px;
  width: 100%;
}

.mySwiper2 {
  // defines height and width of thumbs swiper
  height: 100px;
  width: 100%;
  box-sizing: border-box;
  padding: 10px 0;
  swiper-slide {
    opacity: 0.6; // this set default opacity to all slides
  }
  .swiper-slide-thumb-active {
    opacity: 1; // this reset the opacity one for the active slide
  }
}

ts

import { CommonModule } from '@angular/common';
import {
  Component,
  OnInit,
  CUSTOM_ELEMENTS_SCHEMA,
  ViewChild,
  ElementRef,
  AfterViewInit,
  NgZone,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';


@Component({
  selector: 'app-swiper-thumbs-vertical-dup-1',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './swiper-thumbs-vertical-dup-1.component.html',
  styleUrl: './swiper-thumbs-vertical-dup-1.component.scss',
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SwiperThumbsVerticalDup1Component implements OnInit, AfterViewInit {
  @ViewChild('swiper1') swiper1!: ElementRef<any>;
  @ViewChild('swiper2') swiper2!: ElementRef<any>;

  slides: any[] = []

  constructor(private zone: NgZone, private route: ActivatedRoute) { }

  ngOnInit() {
    this.routeSub = this.route.params.subscribe(params => {
      const id = params['id'];
      if (id == 1) {
        this.slides = [
          { image: 'https://swiperjs.com/demos/images/nature-1.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-2.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-3.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-4.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-5.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-6.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-7.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-8.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-10.jpg' },
        ];
      } else {
        this.slides = [
          { image: 'https://swiperjs.com/demos/images/nature-6.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-7.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-8.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-10.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-1.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-2.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-3.jpg' },
        ];
      }
    });
  }

  ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {
      const swiperParams = {
        breakpoints: {
          100: {
            slidesPerView: 3,
          },
          640: {
            slidesPerView: 5,
          },
          1024: {
            slidesPerView: 6,
          },
        },
      };

      const swiperParams1 = {
        spaceBetween: 10
      };

      Object.assign(this.swiper2.nativeElement, swiperParams1);
      this.swiper1.nativeElement.initialize();

      // now we need to assign all parameters to Swiper element
      Object.assign(this.swiper2.nativeElement, swiperParams);
      this.swiper2.nativeElement.initialize();
    });
  }
}

Solution

  • When we change the route, swiper is getting confused when the *ngFor (slides) are getting updated, so to fix this, we can reinitialize the swiper-container using the below code, which will ensure the swiper still works on params change!

    code to reinitialize Swiper

      if (this.swiper1?.nativeElement) {
        this.swiper1.nativeElement.swiper.destroy();
        this.swiper1.nativeElement.initialized = false;
        this.swiper1.nativeElement.initialize();
      }
      if (this.swiper2?.nativeElement) {
        this.swiper2.nativeElement.swiper.destroy();
        this.swiper2.nativeElement.initialized = false;
        this.swiper2.nativeElement.initialize();
      }
    

    Note: I have removed spaceBetween property since its causing a wierd bug, when the slide movement gets offset by that value as the slides increase, I am unable to solve it, but it works without this property!

    full code

    import { CommonModule } from '@angular/common';
    import {
      Component,
      OnInit,
      CUSTOM_ELEMENTS_SCHEMA,
      ViewChild,
      ElementRef,
    } from '@angular/core';
    import { ActivatedRoute } from '@angular/router';
    import { Subscription } from 'rxjs';
    import { register } from 'swiper/element';
    register();
    @Component({
      selector: 'app-swiper',
      standalone: true,
      imports: [CommonModule],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      templateUrl: './swiper.component.html',
    })
    export class SwiperComponent implements OnInit {
      @ViewChild('swiper1') swiper1!: ElementRef<any>;
      @ViewChild('swiper2') swiper2!: ElementRef<any>;
      routeSub!: Subscription;
    
      slides: any[] = [];
    
      constructor(private route: ActivatedRoute) {}
    
      ngOnInit() {
        this.routeSub = this.route.params.subscribe((params) => {
          const id = params['id'];
          if (id == 1) {
            this.slides = [
              { id: 1, image: 'https://swiperjs.com/demos/images/nature-1.jpg' },
              { id: 2, image: 'https://swiperjs.com/demos/images/nature-2.jpg' },
              { id: 3, image: 'https://swiperjs.com/demos/images/nature-3.jpg' },
              { id: 4, image: 'https://swiperjs.com/demos/images/nature-4.jpg' },
              { id: 5, image: 'https://swiperjs.com/demos/images/nature-5.jpg' },
              { id: 6, image: 'https://swiperjs.com/demos/images/nature-6.jpg' },
              { id: 7, image: 'https://swiperjs.com/demos/images/nature-7.jpg' },
              { id: 8, image: 'https://swiperjs.com/demos/images/nature-8.jpg' },
              { id: 9, image: 'https://swiperjs.com/demos/images/nature-10.jpg' },
            ];
          } else {
            this.slides = [
              { id: 10, image: 'https://swiperjs.com/demos/images/nature-6.jpg' },
              { id: 11, image: 'https://swiperjs.com/demos/images/nature-7.jpg' },
              { id: 12, image: 'https://swiperjs.com/demos/images/nature-8.jpg' },
              { id: 13, image: 'https://swiperjs.com/demos/images/nature-10.jpg' },
              { id: 14, image: 'https://swiperjs.com/demos/images/nature-1.jpg' },
              { id: 15, image: 'https://swiperjs.com/demos/images/nature-2.jpg' },
              { id: 16, image: 'https://swiperjs.com/demos/images/nature-3.jpg' },
            ];
          }
          if (this.swiper1?.nativeElement) {
            this.swiper1.nativeElement.swiper.destroy();
            this.swiper1.nativeElement.initialized = false;
            this.swiper1.nativeElement.initialize();
          }
          if (this.swiper2?.nativeElement) {
            this.swiper2.nativeElement.swiper.destroy();
            this.swiper2.nativeElement.initialized = false;
            this.swiper2.nativeElement.initialize();
          }
        });
      }
    
      ngAfterViewInit() {
        const swiperParams = {
          updateOnWindowResize: true,
          breakpoints: {
            100: {
              slidesPerView: 3,
            },
            640: {
              slidesPerView: 5,
            },
            1024: {
              slidesPerView: 6,
            },
          },
        };
    
        const swiperParams1 = {};
        Object.assign(this.swiper1.nativeElement, swiperParams1);
        this.swiper1.nativeElement.initialize();
    
        // now we need to assign all parameters to Swiper element
        Object.assign(this.swiper2.nativeElement, swiperParams);
        this.swiper2.nativeElement.initialize();
      }
    
      trackBy(index: number, slide: any) {
        return slide.id;
      }
    }
    

    html

    <div>
      <swiper-container
        #swiper1
        style="--swiper-navigation-color: #fff; --swiper-pagination-color: #fff"
        class="mySwiper"
        thumbs-swiper=".mySwiper2"
        navigation="true"
        init="false"
      >
        <swiper-slide *ngFor="let slide of slides">
          <img [src]="slide.image" />
        </swiper-slide>
      </swiper-container>
    
      <swiper-container
        #swiper2
        class="mySwiper2"
        space-between="10"
        slides-per-view="6"
        free-mode="true"
        watch-slides-progress="true"
        init="false"
      >
        <swiper-slide *ngFor="let slide of slides">
          <img [src]="slide.image" />
        </swiper-slide>
      </swiper-container>
    </div>
    

    Stackblitz Demo