Angular 14中如何从另一个组件读取组件属性?
问题
我希望从另一个组件中读取一个字符串属性,搜索到的方案多涉及*ngIf/ngFor,但我无需在HTML文件中展示该属性,仅需读取该属性。
现有代码如下:
export class LoginComponent implements OnInit { // 我想要从其他组件读取的属性 userRefid!: string; onSubmit(): void { const { email, password } = this.form; this.authService.login(email, password).subscribe({ next: data => { this.storageService.saveUser(data); this.isLoginFailed = false; this.isLoggedIn = true; this.roles = this.storageService.getUser().roles; // 从数据库获取值 this.userRefid = this.storageService.getUser().refid; this.reloadPage(); }, error: err => { this.errorMessage = err.error.message; this.isLoginFailed = true; } }); } } export class ModeratorComponent implements OnInit, AfterViewInit { @ViewChildren(LoginComponent) loginComponent!: LoginComponent; currentUserRefid: any; ngAfterViewInit(): void { this.currentUserRefid = this.loginComponent.userRefid; } }
遇到的问题:在ngAfterViewInit函数中,this.loginComponent始终为undefined,报错ERROR TypeError: Cannot set properties of undefined (setting 'userRefid')。想了解应使用ViewChild/ViewChildren还是其他方法实现组件间属性传递。
解决方案
为什么你的ViewChildren写法无效?
ViewChild/ViewChildren仅能获取当前组件模板中直接渲染的子组件实例。如果LoginComponent没有在ModeratorComponent的HTML模板中声明(比如没有写<app-login></app-login>),必然无法获取到实例,返回undefined。- 即使
LoginComponent是子组件,你的写法也有误:@ViewChildren返回的是QueryList<LoginComponent>类型,单个组件实例应该用@ViewChild(LoginComponent)。
更适合你的组件间属性传递方案
结合你已经使用storageService保存用户数据的场景,推荐以下几种无需依赖模板的方案:
1. 直接通过共享服务读取(最优)
你已经用storageService持久化了用户数据,完全可以在ModeratorComponent中直接调用服务获取refid,无需通过LoginComponent中转:
export class ModeratorComponent implements OnInit { currentUserRefid: string | undefined; constructor(private storageService: StorageService) {} ngOnInit(): void { const user = this.storageService.getUser(); if (user) { this.currentUserRefid = user.refid; } } }
如果用户数据是异步更新的(比如登录后才生成),可以在storageService中添加可观察对象,让ModeratorComponent订阅数据变化:
// storage.service.ts import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class StorageService { private userSubject = new BehaviorSubject<any>(null); user$ = this.userSubject.asObservable(); saveUser(user: any) { localStorage.setItem('user', JSON.stringify(user)); this.userSubject.next(user); } getUser() { const user = localStorage.getItem('user'); return user ? JSON.parse(user) : null; } } // ModeratorComponent.ts export class ModeratorComponent implements OnInit, OnDestroy { currentUserRefid: string | undefined; private subscription!: Subscription; constructor(private storageService: StorageService) {} ngOnInit(): void { // 初始获取一次数据 const user = this.storageService.getUser(); if (user) { this.currentUserRefid = user.refid; } // 订阅后续用户数据的变化 this.subscription = this.storageService.user$.subscribe(user => { if (user) { this.currentUserRefid = user.refid; } }); } ngOnDestroy(): void { this.subscription.unsubscribe(); } }
2. 使用RxJS Subject实现跨组件通信
如果不想依赖storageService,可以创建专门的共享服务,用Subject传递属性值:
// user.service.ts import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UserService { private refidSubject = new Subject<string>(); refid$ = this.refidSubject.asObservable(); setRefid(refid: string) { this.refidSubject.next(refid); } } // LoginComponent.ts export class LoginComponent implements OnInit { constructor(private authService: AuthService, private storageService: StorageService, private userService: UserService) {} onSubmit(): void { const { email, password } = this.form; this.authService.login(email, password).subscribe({ next: data => { this.storageService.saveUser(data); const refid = this.storageService.getUser().refid; this.userRefid = refid; this.userService.setRefid(refid); // 发送属性值 this.reloadPage(); }, // ... 错误处理逻辑 }); } } // ModeratorComponent.ts export class ModeratorComponent implements OnInit, OnDestroy { currentUserRefid: string | undefined; private subscription!: Subscription; constructor(private userService: UserService) {} ngOnInit(): void { this.subscription = this.userService.refid$.subscribe(refid => { this.currentUserRefid = refid; }); } ngOnDestroy(): void { this.subscription.unsubscribe(); } }
3. 什么时候适合用ViewChild?
只有当ModeratorComponent是LoginComponent的父组件,且LoginComponent直接在ModeratorComponent的模板中渲染时,才适合使用ViewChild:
<!-- moderator.component.html --> <app-login></app-login>
// ModeratorComponent.ts import { ViewChild, AfterViewInit } from '@angular/core'; import { LoginComponent } from './login.component'; export class ModeratorComponent implements AfterViewInit { @ViewChild(LoginComponent) loginComponent!: LoginComponent; currentUserRefid: string | undefined; ngAfterViewInit(): void { // 注意:这里只能获取初始值,若userRefid是异步更新的,需监听属性变化 this.currentUserRefid = this.loginComponent.userRefid; // 可通过setTimeout或属性监听获取异步更新后的值 } }
这种方式局限性大,仅适用于直接父子关系的组件,不适合跨路由、非嵌套的组件通信。
内容的提问来源于stack exchange,提问作者hansvdzz
相关产品推荐
相关产品推荐

