Angular 10中如何通过另一组件的点击事件修改iframe的src属性
问题描述
我需要实现点击菜单栏时修改iframe的src属性,菜单栏为独立组件,其下拉菜单支持切换语言,我希望根据选中的语言动态调整iframe的src。
菜单HTML代码如下,其中绑定了名为updateSrc()的方法:
<nav> <div class="select-box"> <div class="select-box__current" tabindex="1"> <div class="select-box__value" (click)="updateSrc(first_url)"> <input class="select-box__input" type="radio" id="0" value="1" name="Ben" checked="checked"/> <p class="select-box__input-text">Es</p> </div> <div class="select-box__value" (click)="updateSrc(second_url)"> <input class="select-box__input" type="radio" id="1" value="2" name="Ben"/> <p class="select-box__input-text">En</p> </div> <div class="select-box__value"> <input class="select-box__input" type="radio" id="2" value="3" name="Ben"/> <p class="select-box__input-text">It</p> </div> <img class="select-box__icon" src="http://cdn.onlinewebfonts.com/svg/img_295694.svg" alt="Arrow Icon" aria-hidden="true"/> </div> <ul class="select-box__list"> <li> <label class="select-box__option" for="0" aria-hidden="aria-hidden">Es</label> </li> <li> <label class="select-box__option" for="1" aria-hidden="aria-hidden">En</label> </li> <li> <a href="https://esourcecapital.it/"> <label class="select-box__option" aria-hidden="aria-hidden">It</label> </a> </li> </ul> </div> </nav>
header组件的TS代码:
import { Component, OnInit } from '@angular/core'; import { DomSanitizer, SafeResourceUrl, SafeUrl } from '@angular/platform-browser'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.scss'] }) export class HeaderComponent implements OnInit { menu:boolean = false; constructor(private translate: TranslateService, private sanitizer: DomSanitizer) { } ngOnInit(): void { } first_url = "https://www.youtube.com/embed/4oP20QZxahk"; second_url = "https://www.youtube.com/embed/Q_ZPBqVF0_8"; current_url: SafeUrl; updateSrc(url) { this.current_url=this.sanitizer.bypassSecurityTrustResourceUrl(url); } }
需要修改的iframe位于另一个组件中,对应代码如下:
<div class="center"> <iframe width="640" height="360" id="frame" frameborder="0" src="https://www.youtube.com/embed/4oP20QZxahk" [src]="current_url" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <!-- <div class="pairs"> <button md-button (click)="updateSrc(first_url)" id="first" class="top-link">Video en español</button> <button md-button (click)="updateSrc(second_url)" id="second" class="top-link">Video in english</button> </div> --> </div>
上述代码在同一组件中可正常运行,但菜单与iframe分属不同组件时无法生效,请问该如何解决?
解决方案
你遇到的是Angular跨组件数据同步的问题,两个独立组件无法直接访问对方的内部变量,可根据组件的层级关系选择以下两种方案:
方案一:父子组件层级,用@Output+@Input中转
如果两个组件有共同的父组件,可通过父组件做数据中转:
- 修改header组件,对外抛出url变更事件
// header组件TS新增导入 import { Output, EventEmitter } from '@angular/core'; // 类内部新增输出事件 @Output() urlChange = new EventEmitter<string>(); // 修改updateSrc方法 updateSrc(url: string) { this.urlChange.emit(url); }
- 父组件监听header的url变更事件,存储为公共变量
// 父组件TS import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; export class ParentComponent { currentIframeUrl: SafeUrl; constructor(private sanitizer: DomSanitizer) {} handleUrlChange(url: string) { this.currentIframeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url); } }
<!-- 父组件模板 --> <app-header (urlChange)="handleUrlChange($event)"></app-header> <!-- 把url传给iframe所在的子组件 --> <app-video-page [iframeUrl]="currentIframeUrl"></app-video-page>
- iframe所在组件新增输入属性接收url
// iframe组件TS import { Input } from '@angular/core'; import { SafeUrl } from '@angular/platform-browser'; export class VideoPageComponent { @Input() iframeUrl: SafeUrl; }
<!-- iframe组件模板修改src绑定 --> <iframe width="640" height="360" id="frame" frameborder="0" [src]="iframeUrl" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
方案二:无直接层级关系,用公共服务共享状态
如果两个组件层级嵌套深、或者是平级无关组件,可通过全局共享服务实现状态同步:
- 新建全局共享服务
video.service.ts
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; @Injectable({ providedIn: 'root' }) export class VideoService { // 初始默认url为西语视频 private videoUrlSub = new BehaviorSubject<string>('https://www.youtube.com/embed/4oP20QZxahk'); videoUrl$ = this.videoUrlSub.asObservable(); constructor(private sanitizer: DomSanitizer) {} updateUrl(url: string) { this.videoUrlSub.next(url); } getSafeUrl(url: string): SafeUrl { return this.sanitizer.bypassSecurityTrustResourceUrl(url); } }
- header组件注入服务,点击时更新服务内的url
// header组件TS constructor( private translate: TranslateService, private sanitizer: DomSanitizer, private videoService: VideoService ) {} updateSrc(url: string) { this.videoService.updateUrl(url); }
- iframe所在组件注入服务,订阅url变更
// iframe组件TS import { Subscription } from 'rxjs'; export class VideoPageComponent implements OnInit, OnDestroy { currentUrl: SafeUrl; private sub: Subscription; constructor(private videoService: VideoService) {} ngOnInit() { this.sub = this.videoService.videoUrl$.subscribe(url => { this.currentUrl = this.videoService.getSafeUrl(url); }); } ngOnDestroy() { // 销毁时取消订阅避免内存泄漏 this.sub?.unsubscribe(); } }
<!-- iframe组件模板直接绑定currentUrl --> <iframe width="640" height="360" id="frame" frameborder="0" [src]="currentUrl" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
内容的提问来源于stack exchange,提问作者Luis Alejandro López de Anda
相关产品推荐
相关产品推荐

