Vijai S
Vijai S

Reputation: 151

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();
    });
  }
}

Upvotes: 2

Views: 1334

Answers (2)

swiper.component.ts

afterNextRender(() => {
  Object.assign(this.swiperElementRef.nativeElement, this.customSwiperOptions);
  this.swiperElementRef.nativeElement.initialize();
});

main.ts (If you are using Angular SSR)

import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { register as registerSwiperElements } from 'swiper/element/bundle';

// Register Swiper custom elements. We do this
// before bootstrapping the Angular application
// so that they're available before any part of
// our application tries rendering them.
registerSwiperElements();

bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));

Upvotes: 0

Naren Murali
Naren Murali

Reputation: 57986

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

Upvotes: 2

Related Questions