Angular Swiper Only Shows One Slide Initially Before Loading Others

Discussão em 'Angular' iniciado por Francisco Soares, Fevereiro 21, 2025.

  1. import { Component } from '@angular/core';
    import { MatButtonToggleModule } from '@angular/material/button-toggle';
    import { TmdbService } from '../../services/tmdb.service';
    import { OnInit, OnDestroy } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { FormsModule } from '@angular/forms';
    import { Subject, takeUntil } from 'rxjs';
    import { ITrending } from '../../interfaces/trending';
    import Swiper from 'swiper';
    import { RouterLink } from '@angular/router';
    import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
    import { swiperInitialization } from '../../swiper/swiperConfig';
    import { ShowCardScoreComponent } from '../show-card-score/show-card-score.component';

    @Component({
    selector: 'app-trending',
    imports: [
    MatButtonToggleModule,
    ShowCardScoreComponent,
    CommonModule,
    FormsModule,
    RouterLink,
    NgxSkeletonLoaderModule,
    ],
    templateUrl: './trending.component.html',
    styleUrl: './trending.component.css',
    })
    export class TrendingComponent implements OnInit, OnDestroy {
    loading: boolean = false;
    timeSelectedValue: string = 'day';
    trendingShows: Array<ITrending> = [];
    swiper: Swiper | null = null;

    $unsubscribe: Subject<void> = new Subject<void>();

    constructor(private tmdbService: TmdbService) {}

    ngOnInit(): void {
    this.getTrendingData(this.timeSelectedValue);
    }

    ngOnDestroy(): void {
    this.$unsubscribe.next();
    this.$unsubscribe.complete();
    if (this.swiper) {
    this.swiper.destroy(true, true);
    }
    }

    // This method is triggered when the selection changes
    onSelectionChange(event: any) {
    this.timeSelectedValue = event.value;
    this.getTrendingData(this.timeSelectedValue);
    }

    getTrendingData(timeSelectedValue: string) {
    this.trendingShows = [];
    this.loading = true;
    if (this.swiper) {
    this.swiper.destroy(true, true); // Destroy the current Swiper instance
    }
    this.swiper = swiperInitialization('#swiperTrending', {
    300: { slidesPerView: 1 },
    500: { slidesPerView: 2 },
    900: { slidesPerView: 4 },
    1200: { slidesPerView: 5 },
    1400: { slidesPerView: 6 },
    1800: { slidesPerView: 8 },
    });

    this.tmdbService
    .getTrendingAll(timeSelectedValue)
    .pipe(takeUntil(this.$unsubscribe))
    .subscribe((data) => {
    console.log(data);
    // Process data here
    data.results.map((show: any) => {
    if (show.name !== undefined) show['title'] = show.name;
    this.trendingShows.push(show);
    });
    this.loading = false;
    });
    }
    }

    <div class="container-fluid mb-3">
    <div class="trendingContainer container">
    <div class="row align-items-center">
    <div class="col-md-4 mt-3">
    <h3>Trending</h3>
    </div>
    <div class="col-md-8 mt-3" *ngIf="timeSelectedValue">
    <mat-button-toggle-group
    name="fontStyle"
    aria-label="Font Style"
    (change)="onSelectionChange($event)"
    [value]="timeSelectedValue"
    >
    <mat-button-toggle value="day">Today</mat-button-toggle>
    <mat-button-toggle value="week">This Week</mat-button-toggle>
    </mat-button-toggle-group>
    </div>
    </div>
    </div>
    <!-- Skeleton loaders: show 8 when loading -->
    <div class="d-flex mt-3" *ngIf="loading">
    <div
    *ngFor="let _ of [].constructor(8); let i = index" class="d-flex"
    >
    <div class="skeleton-card-item">
    <!-- Card image skeleton -->
    <div class="position-relative">
    <ngx-skeleton-loader
    [theme]="{
    'border-radius': '12px',
    height: '292px',
    width: '192px',
    margin: '0px 10px'
    }"
    ></ngx-skeleton-loader>
    <!-- Score placeholder skeleton -->
    <ngx-skeleton-loader
    [theme]="{
    'border-radius': '12px',
    height: '50px',
    width: '50px',
    position: 'absolute',
    top: '100%',
    left: '100%',
    transform: 'translate(-50%, -50%)'
    }"
    ></ngx-skeleton-loader>
    </div>

    <!-- Title placeholder skeleton -->
    <ngx-skeleton-loader
    appearance="line"
    [theme]="{
    'border-radius': '12px',
    width: '198px',
    'margin-top': '30px'
    }"
    ></ngx-skeleton-loader>

    <!-- Watchlist button placeholder skeleton -->
    <ngx-skeleton-loader
    appearance="line"
    [theme]="{
    'border-radius': '24px',
    height: '40px',
    width: '150px',
    margin: '10px auto'
    }"
    ></ngx-skeleton-loader>
    </div>
    </div>
    </div>
    <div id="swiperTrending" class="swiper-container mt-5 mb-5">
    <div class="swiper-wrapper">
    @for(show of trendingShows;track show.id;let index=$index){
    <div class="swiper-slide">
    <app-show-card-score
    [show]="show"
    [index]="index"
    [routerLink]="
    show.media_type === 'movie'
    ? ['/movie', show.id]
    : show.media_type === 'tv'
    ? ['/tv', show.id]
    : ['/unknown', show.id]
    "
    ></app-show-card-score>
    </div>
    }
    </div>
    </div>
    </div>

    I'm using Angular with Swiper.js to display a trending list of shows fetched from the TMDB API. However, when the component loads, only one slide appears at first, and then after a brief moment, all slides load in. I want all slides to appear immediately after data is fetched.

    I have tried this but none worked:

    -Initializing Swiper after fetching the data.

    -Destroying and reinitializing Swiper before loading new data.

    -Making sure trendingShows updates before Swiper initializes.

    -Delaying Swiper initialization slightly using setTimeout.

    Firs shows the normal skeleton before data is fetched [​IMG]

    When data is fetched flashes this at first [​IMG]

    and then this [​IMG]

    Continue reading...

Compartilhe esta Página