Angular中Bootstrap轮播嵌入YouTube视频报e为null错误如何解决
问题原因
报错Uncaught TypeError: can't access property "classList", e is null核心由3个问题导致:
- 轮播初始化时机和异步数据加载不匹配:你给轮播加了
data-bs-ride="carousel"属性,会让Bootstrap在页面加载完成后立刻初始化轮播,但此时sortedMovies是空数组,轮播内部没有任何幻灯片条目,等接口返回数据渲染完新的幻灯片后,轮播内部的索引映射还是旧的,滑动时找不到对应元素就抛出空指针错误 active类绑定逻辑错误:Bootstrap轮播要求初始状态下只能有1个幻灯片和对应指示器处于激活状态,你当前写的[ngClass]="{active:i}"会给所有索引大于0的元素都加上active类,导致轮播逻辑混乱- 指示器的
data-bs-slide-to属性重复定义:你同时写了静态的data-bs-slide-to="i"和动态的[attr.data-slide-to]="i",静态属性会覆盖动态绑定,导致所有指示器跳转的都是错误索引
修复步骤
1. 修正HTML代码
<div id="home-carousel" class="container p-0"> <!-- 加*ngIf控制渲染时机,数据加载完成后再渲染轮播 或者你也可以选择手动初始化轮播,两种方案选一个就行 --> <div id="carouselDark" class="carousel carousel-dark slide" *ngIf="sortedMovies.length > 0" data-bs-ride="carousel" > <div class="carousel-indicators"> <button *ngFor="let movie of sortedMovies; let i = index" type="button" data-bs-target="#carouselDark" <!-- 删掉静态写死的data-bs-slide-to="i" --> [ngClass]="{'active': i === 0}" <!-- 只有第一个指示器加active --> aria-current="true" [attr.aria-label]="'Slide ' + (i+1)" [attr.data-bs-slide-to]="i" ></button> </div> <div class="carousel-inner"> <div *ngFor="let movie of sortedMovies; let i = index" class="carousel-item" data-bs-interval="2000" [ngClass]="{'active': i === 0}" <!-- 只有第一个幻灯片加active --> > <iframe [src]='getSafeURL(movie.youtube)' height="500" class="d-block w-100"></iframe> <div class="carousel-caption d-none d-md-block"> <h2 class="text-white film-title">{{movie.title}}</h2> <a [routerLink]="'/movie/' + movie.id">Zum Film</a> </div> </div> </div> <button class="carousel-control-prev" type="button" data-bs-target="#carouselDark" data-bs-slide="prev"> <span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="visually-hidden">Previous</span> </button> <button class="carousel-control-next" type="button" data-bs-target="#carouselDark" data-bs-slide="next"> <span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="visually-hidden">Next</span> </button> </div> </div>
2. 可选:手动初始化轮播(不用*ngIf的话选这个方案)
如果不想用*ngIf控制渲染,也可以手动管理轮播实例,修改TS代码:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { environment } from 'src/environments/environment'; import { Movie } from '../../models/movie.model'; import { PublicService } from '../../services/public.service'; import { Carousel } from 'bootstrap'; // 导入Bootstrap轮播类 @Component({ selector: 'app-slider', templateUrl: './slider.component.html', styleUrls: ['./slider.component.css'] }) export class SliderComponent implements OnInit, OnDestroy { movies: Movie[] = []; sortedMovies: Movie[]=[]; currentIndex: number = 1; BACKEND_URL = environment.apiUrl private carousel: Carousel | null = null; // 存轮播实例 constructor(private publicService: PublicService, private sanitizer: DomSanitizer) { } ngOnInit(): void { this.publicService.getAllPublicMovies().subscribe((data) => { this.movies = data; this.sortedMovies = this.sortMovies(data); // 数据渲染完成后手动初始化轮播 setTimeout(() => { const carouselEl = document.getElementById('carouselDark'); if (carouselEl) { this.carousel = new Carousel(carouselEl, { interval: 2000, ride: 'carousel' }) } }, 0) }); } // 组件销毁时销毁轮播实例,避免内存泄漏 ngOnDestroy(): void { if (this.carousel) { this.carousel.dispose(); } } getSafeURL(youtubeLink: string){ let youtubeId = this.getYoutubeId(youtubeLink); let returnURL = 'https://www.youtube.com/embed/'+youtubeId; let safeURL = this.sanitizer.bypassSecurityTrustResourceUrl(returnURL); return safeURL; } getYoutubeId(youtubeLink: string) { let regex = new RegExp(/(?:\?v=)([^&]+)(?:&)*/); let matches = regex.exec(youtubeLink); let youtubeId = matches![1]; return youtubeId; } sortMovies(movies: Movie[]){ let sortedMovies: Movie[]; sortedMovies = movies.sort((a,b) => (a.cinemaRelease > b.cinemaRelease ? 1 : -1)); return sortedMovies; } }
内容的提问来源于stack exchange,提问作者Svensen
相关产品推荐
相关产品推荐

