Angular 14中从父组件刷新多个子组件的问题
Angular 父组件刷新多个同类型子组件的解决方案
场景复现
子组件代码
import { Component } from "@angular/core"; @Component({ selector: "app-child", template: `<p> {{ticks}} </p>`, styleUrls: ["./child.component.css"] }) export class ChildComponent { ticks = Date.now().valueOf(); constructor() {} update(): void { this.ticks = Date.now().valueOf(); } }
父组件代码
import { Component, OnInit, ViewChild } from '@angular/core'; import { ChildComponent } from './../child/child.component'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent implements OnInit { @ViewChild(ChildComponent, { static: false }) childC: ChildComponent; showChild: boolean = true; constructor() {} ngOnInit() {} onUpdateChild() { this.childC.update(); } }
父组件HTML
<p> parent works! <button (click)="onUpdateChild()">update</button> <app-child *ngIf="showChild"></app-child> <app-child *ngIf="showChild"></app-child> </p>
核心问题
父组件中多次引用同一子组件时,点击update按钮仅能刷新第一个子组件,需实现点击按钮后所有子组件同步刷新。
问题原因
@ViewChild装饰器只会获取DOM中第一个匹配的子组件实例,因此调用childC.update()仅能作用于第一个子组件。
解决方案
方案1:使用@ViewChildren获取所有子组件实例
通过@ViewChildren替代@ViewChild,获取所有ChildComponent实例,遍历调用更新方法:
import { Component, OnInit, ViewChildren, QueryList } from '@angular/core'; import { ChildComponent } from './../child/child.component'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent implements OnInit { @ViewChildren(ChildComponent) childComponents!: QueryList<ChildComponent>; showChild: boolean = true; constructor() {} ngOnInit() {} onUpdateChild() { this.childComponents.forEach(child => child.update()); } }
方案2:通过@Input属性触发变更检测
遵循Angular单向数据流理念,通过输入属性的变化触发子组件刷新:
- 修改子组件,添加输入属性并监听变化:
import { Component, Input, OnChanges, SimpleChanges } from "@angular/core"; @Component({ selector: "app-child", template: `<p> {{ticks}} </p>`, styleUrls: ["./child.component.css"] }) export class ChildComponent implements OnChanges { ticks = Date.now().valueOf(); @Input() refreshTrigger: number = 0; ngOnChanges(changes: SimpleChanges): void { if (changes['refreshTrigger']) { this.ticks = Date.now().valueOf(); } } }
- 修改父组件,添加触发变量:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent implements OnInit { showChild: boolean = true; refreshCount: number = 0; constructor() {} ngOnInit() {} onUpdateChild() { this.refreshCount++; } }
- 更新父组件HTML,传递触发变量:
<p> parent works! <button (click)="onUpdateChild()">update</button> <app-child *ngIf="showChild" [refreshTrigger]="refreshCount"></app-child> <app-child *ngIf="showChild" [refreshTrigger]="refreshCount"></app-child> </p>
方案3:使用共享服务实现跨组件通信
创建共享服务,通过RxJS事件通知所有子组件刷新:
- 创建刷新服务:
import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RefreshService { private refreshSubject = new Subject<void>(); refresh$ = this.refreshSubject.asObservable(); triggerRefresh() { this.refreshSubject.next(); } }
- 修改子组件,订阅刷新事件:
import { Component, OnInit, OnDestroy } from "@angular/core"; import { RefreshService } from './refresh.service'; import { Subscription } from 'rxjs'; @Component({ selector: "app-child", template: `<p> {{ticks}} </p>`, styleUrls: ["./child.component.css"] }) export class ChildComponent implements OnInit, OnDestroy { ticks = Date.now().valueOf(); private refreshSub!: Subscription; constructor(private refreshService: RefreshService) {} ngOnInit(): void { this.refreshSub = this.refreshService.refresh$.subscribe(() => { this.ticks = Date.now().valueOf(); }); } ngOnDestroy(): void { this.refreshSub.unsubscribe(); } }
- 修改父组件,调用服务触发刷新:
import { Component, OnInit } from '@angular/core'; import { RefreshService } from './refresh.service'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent implements OnInit { showChild: boolean = true; constructor(private refreshService: RefreshService) {} ngOnInit() {} onUpdateChild() { this.refreshService.triggerRefresh(); } }
内容的提问来源于stack exchange,提问作者farshad fahimi
相关产品推荐
相关产品推荐

