javascriptangulartypescriptswiper.jsreinitialization

SwiperJs not working properly when rendering it conditionally


In my angular application, I have 4 swiper slide, which i want to render conditionally. But its not working properly. I have added images as well at bottom.

Issue - On initial load all swiper are rendering properly with 4 slides per view. But suppose if select swiper_1 then its showing 1 slide per view but according to swiperParam it should show 4 slide per view. If I select others like swiper_2, or swiper_3, then its showing 4 slides per view, and then if i select all then all swiper are showing 4 slide per view except the previous selected swiper "swiper_2 or swiper_3".

I think the parameters are not getting applied properly.

Typescript ->

import { CommonModule } from '@angular/common';
import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, NgZone, QueryList, Renderer2, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-swiper-testing',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './swiper-testing.component.html',
  styleUrls: ['./swiper-testing.component.scss'],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  encapsulation: ViewEncapsulation.None,
})
export class SwiperTestingComponent {

  constructor(private el: ElementRef, private cdRef: ChangeDetectorRef, private ngZone: NgZone, private renderer: Renderer2) { }

  @ViewChild('swiper_1') swiper_1!: ElementRef;
  @ViewChild('swiper_2') swiper_2!: ElementRef;
  @ViewChild('swiper_3') swiper_3!: ElementRef;
  @ViewChild('swiper_4') swiper_4!: ElementRef;

  selectedCategories: string = 'all';

  swiperParams = {
    loop: false,
    slidesPerView: 1,
    spaceBetween: 24,
    breakpoints: {
      640: {
        slidesPerView: 2,
      },
      1024: {
        slidesPerView: 3,
      },
      1200: {
        slidesPerView: 4,
      },
      1500: {
        slidesPerView: 4.5,
      },
      1700: {
        slidesPerView: 5,
      },
      1900: {
        slidesPerView: 5.5,
      },
    },
    navigation: true,
  };
  
  
  ngAfterViewInit() {
    this.swiper_1Swiper();
    this.swiper_2Swiper();
    this.swiper_3Swiper();
    this.swiper_4Swiper();
  }

  categoriesSelect(value: string) {
    if (this.selectedCategories === value) return;
  
    this.selectedCategories = value;
    this.cdRef.detectChanges();
  
    // Let Angular fully update the view first
    this.ngZone.runOutsideAngular(() => {
      setTimeout(() => {
        this.ngZone.run(() => {
          if (value === 'all' || value === 'swiper_1') this.swiper_1Swiper();
          if (value === 'all' || value === 'swiper_2') this.swiper_2Swiper();
          if (value === 'all' || value === 'swiper_3') this.swiper_3Swiper();
          if (value === 'all' || value === 'swiper_4') this.swiper_4Swiper();
        });
      }, 0);
    });
  }
  
  swiper_1Swiper() {
    if (this.swiper_1?.nativeElement) {
      const swiperEl = this.swiper_1.nativeElement;
      Object.assign(swiperEl, this.swiperParams );
      swiperEl.initialize();
    }
  }
  
  
  swiper_2Swiper() {
    if (this.swiper_2?.nativeElement) {
      const swiperEl = this.swiper_2.nativeElement;
      Object.assign(swiperEl, this.swiperParams);
      swiperEl.initialize();
    }
  }
  swiper_3Swiper() {
    if (this.swiper_3?.nativeElement) {
      const swiperEl = this.swiper_3.nativeElement;
      Object.assign(swiperEl, this.swiperParams);
      swiperEl.initialize();
    }
  }
  swiper_4Swiper() {
    if (this.swiper_4?.nativeElement) {
      const swiperEl = this.swiper_4.nativeElement;
      Object.assign(swiperEl, this.swiperParams);
      swiperEl.initialize();
    }
  }
  

}

HTML ->

<div>
    <div class="main">
        <div class="filter-container d-flex justify-content-between align-items-start">
            <button type="button" class="btn btn-primary selected" (click)="categoriesSelect('all')">All
                swiper</button>
            <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_1')">swiper 1</button>
            <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_2')">swiper 2</button>
            <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_3')">swiper 3</button>
            <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_4')">swiper 4</button>
        </div>
        <div class="d-flex flex-column gap-4 mt-48">

            <div class="cards-swiper-container"
                *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_1'">
                <h4 class="mb-0">swiper_1 </h4>
                <div class="navigation-wrapper"></div>
                <swiper-container class="mySwiper" init="false" #swiper_1>
                    <swiper-slide *ngFor="let item of [1,2,3,4,5,6,7,8];">
                    </swiper-slide>
                </swiper-container>
            </div>
            <div class="cards-swiper-container" *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_2'">
                <h4 class="mb-0">swiper_2 </h4>
                <div class="navigation-wrapper"></div>
                <swiper-container class="mySwiper" init="false" #swiper_2>
                    <swiper-slide *ngFor="let item of [1,2,3,4,5,6,7,8];">
                    </swiper-slide>
                </swiper-container>
            </div>
            <div class="cards-swiper-container"
                *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_3'">
                <h4 class="mb-0">swiper_3 </h4>
                <div class="navigation-wrapper"></div>
                <swiper-container class="mySwiper" init="false" #swiper_3>
                    <swiper-slide *ngFor="let item of [1,2,3,4,5,6,7,8];">
                    </swiper-slide>
                </swiper-container>
            </div>
            <div class="cards-swiper-container" *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_4'">
                <h4 class="mb-0">swiper_4 </h4>
                <div class="navigation-wrapper"></div>
                <swiper-container class="mySwiper" init="false" #swiper_4>
                    <swiper-slide *ngFor="let item of [1,2,3,4,5,6,7,8];">
                    </swiper-slide>
                </swiper-container>
            </div>
        </div>
    </div>
</div>

All selected ->

enter image description here

Selected Swiper_1 ->

enter image description here

Selected Swiper_2 ->

enter image description here

Selected All again ->

enter image description here


Solution

  • To create more readable code, create a swiper component which encapsulates the swiper code.

    The advantage of this, is when the component is created/destroyed. You will have a fresh initialization of each element.

    @Component({
      selector: 'app-swiper-element',
      standalone: true,
      imports: [],
      template: `
        <swiper-container class="mySwiper" init="false" #swiper>
          <swiper-slide *ngFor="let item of items;">
          </swiper-slide>
        </swiper-container>
      `,
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      encapsulation: ViewEncapsulation.None,
    })
    export class SwiperElementComponent {
      @Input() items = [1,2,3,4,5,6,7,8];
      @ViewChild('swiper') swiper!: ElementRef;
      @Input() swiperParams: any = null;
    
      ngAfterViewInit() {
        this.initialize();
      }
    
      // make swiper react dynamically to @Input changes
      // ngOnChanges() {
      //   this.initialize();
      // }
    
      initialize() {
        if (this.swiper?.nativeElement &&  this.swiperParams) {
          const swiperEl = this.swiper.nativeElement;
          Object.assign(swiperEl, this.swiperParams );
          swiperEl.initialize();
          // this.cdRef.detectChanges(); // <- add this if needed.
        }
      }
    }
    

    Now all we need to do is to add SwiperElementComponent to the imports array of the parent component and simplify the code as below.

    @Component({
      selector: 'app-swiper-testing',
      standalone: true,
      imports: [CommonModule, SwiperElementComponent], // <- add here!
      templateUrl: './swiper-testing.component.html',
      styleUrls: ['./swiper-testing.component.scss'],
      encapsulation: ViewEncapsulation.None,
    })
    export class SwiperTestingComponent {
    
      constructor(private el: ElementRef, private cdRef: ChangeDetectorRef, private ngZone: NgZone, private renderer: Renderer2) { }
    
      selectedCategories: string = 'all';
    
      swiperParams = {
        loop: false,
        slidesPerView: 1,
        spaceBetween: 24,
        breakpoints: {
          640: {
            slidesPerView: 2,
          },
          1024: {
            slidesPerView: 3,
          },
          1200: {
            slidesPerView: 4,
          },
          1500: {
            slidesPerView: 4.5,
          },
          1700: {
            slidesPerView: 5,
          },
          1900: {
            slidesPerView: 5.5,
          },
        },
        navigation: true,
      };
    
      categoriesSelect(value: string) {
        if (this.selectedCategories === value) return;
      
        this.selectedCategories = value;
        this.cdRef.detectChanges();
      }
    }
    

    In the HTML, just pass in the params to this component.

    <div>
        <div class="main">
            <div class="filter-container d-flex justify-content-between align-items-start">
                <button type="button" class="btn btn-primary selected" (click)="categoriesSelect('all')">All
                    swiper</button>
                <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_1')">swiper 1</button>
                <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_2')">swiper 2</button>
                <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_3')">swiper 3</button>
                <button type="button" class="btn btn-primary" (click)="categoriesSelect('swiper_4')">swiper 4</button>
            </div>
            <div class="d-flex flex-column gap-4 mt-48">
    
                <div class="cards-swiper-container"
                    *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_1'">
                    <h4 class="mb-0">swiper_1 </h4>
                    <div class="navigation-wrapper"></div>
                    <app-swiper-element [swiperParams]="swiperParams">
                    </app-swiper-element>
                </div>
                <div class="cards-swiper-container" *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_2'">
                    <h4 class="mb-0">swiper_2 </h4>
                    <app-swiper-element [swiperParams]="swiperParams">
                    </app-swiper-element>
                </div>
                <div class="cards-swiper-container"
                    *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_3'">
                    <h4 class="mb-0">swiper_3 </h4>
                    <div class="navigation-wrapper"></div>
                    <app-swiper-element [swiperParams]="swiperParams">
                    </app-swiper-element>
                </div>
                <div class="cards-swiper-container" *ngIf="selectedCategories === 'all' || selectedCategories === 'swiper_4'">
                    <h4 class="mb-0">swiper_4 </h4>
                    <div class="navigation-wrapper"></div>
                    <app-swiper-element [swiperParams]="swiperParams">
                    </app-swiper-element>
                </div>
            </div>
        </div>
    </div>