Angular框架中的父子组件通信传递异步的数据接收值异常的问题
2021-12-08 本文已影响0人
听书先生
父组件传递给子组件的值为async data
异步数据,子组件接收的过程中可能就会出现问题,子组件接收不到父组件传递过来的值,此时视图也无法进行渲染。
- 子组件message组件:
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Post, GroupPosts } from '../post.interface';
// 定义接口
export interface Post {
title: string;
category: string;
}
export interface GroupPosts {
category: string;
posts: Post[];
}
@Component({
selector: 'exe-message',
template: `
<div class="list-group">
<div *ngFor="let group of groupPosts;" class="list-group-item">
<h4>{{ group.category }}</h4>
<ul>
<li *ngFor="let post of group.posts">
{{ post.title }}
</li>
</ul>
<div>
</div>
`
})
export class ExeMessageComponent implements OnInit {
_data = [];
@Input()
set data(value: any) {
this._data = value;
}
get data() {
return this._data;
}
groupPosts: GroupPosts[] = [];
ngOnInit() {
this.groupPosts = this.groupByCategory(this.data);
}
ngOnChanges(changes: SimpleChanges) {
}
groupByCategory(data: Post[]): GroupPosts[] {
if (!data) return [];
// 去重处理
const categories = new Set(data.map(x => x.category));
// 转为二维数组
const result = Array.from(categories).map(x => ({
category: x,
posts: data.filter(post => post.category === x)
}));
return result;
}
}
- 父组件User组件部分:
import { Component, OnInit, Input, SimpleChanges } from '@angular/core';
import { Observable, Observer } from 'rxjs';
// 定义接口
export interface Post {
title: string;
category: string;
}
export interface GroupPosts {
category: string;
posts: Post[];
}
@Component({
selector: 'exe-bloggers',
template: `
<h1>{{ blogger }}</h1>
<div>
<exe-message [data]="posts"></exe-message >
</div>
`
})
export class KeysComponent implements OnInit {
blogger = 'catalogue';
posts: Post[] = [];
ngOnInit() {
this.getPosts().subscribe(posts => this.posts = posts);
}
getPosts(): Observable<Post[]> {
return Observable.create((observer: Observer<Post[]>) => {
setTimeout(() => {
const posts = [
{ "title": "containing a catalogue", "category": "react" },
{ "title": "and an agreement.", "category": "vue" },
{ "title": "show that we have sent", "category": "react" },
{ "title": "gives a full description of each product", "category": "angular" },
{ "title": "Topshop has coined an online category ", "category": "react" },
{ "title": "The catalogue gives a full description", "category": "vue" }
];
observer.next(posts);
}, 2000);
})
}
}
image.png
根据结果来看,子组件并未接收到,获取的值是一个空数组。
2、解决方案
-
使用ngOnChanges() :
当数据绑定输入属性的值发生变化的时候,Angular内部将会主动调用 ngOnChanges() 方法。它会获得一个 SimpleChanges 对象,包含绑定属性的新值和旧值,因此利用 ngOnChanges() 钩子函数,执行子组件的数据初始化的操作。
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Post, GroupPosts } from '../post.interface';
// 定义接口
export interface Post {
title: string;
category: string;
}
export interface GroupPosts {
category: string;
posts: Post[];
}
@Component({
selector: 'exe-message',
template: `
<div class="list-group">
<div *ngFor="let group of groupPosts;" class="list-group-item">
<h4>{{ group.category }}</h4>
<ul>
<li *ngFor="let post of group.posts">
{{ post.title }}
</li>
</ul>
<div>
</div>
`
})
export class ExeMessageComponent implements OnInit {
_data = [];
@Input()
set data(value: any) {
this._data = value;
}
get data() {
return this._data;
}
groupPosts: GroupPosts[] = [];
ngOnInit() {
this.groupPosts = this.groupByCategory(this.data);
}
ngOnChanges(changes: SimpleChanges) {
console.log(changes)
if (changes['data']) {
this.groupPosts = this.groupByCategory(this.data);
}
}
groupByCategory(data: Post[]): GroupPosts[] {
if (!data) return [];
// 去重处理
const categories = new Set(data.map(x => x.category));
// 转为二维数组
const result = Array.from(categories).map(x => ({
category: x,
posts: data.filter(post => post.category === x)
}));
return result;
}
}
image.png
image.png
-
使用RxJS中的BehaviorSubject:
利用 RxJS 中 BehaviorSubject 来监测变化。需要注意的是,在使用 Observable时,在不使用的时候,需要在ngOnDestroy钩子在去取消订阅,以避免出现内存泄露的问题。
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { Post, GroupPosts } from '../post.interface';
// 定义接口
export interface Post {
title: string;
category: string;
}
export interface GroupPosts {
category: string;
posts: Post[];
}
@Component({
selector: 'exe-message',
template: `
<div class="list-group">
<div *ngFor="let group of groupPosts;" class="list-group-item">
<h4>{{ group.category }}</h4>
<ul>
<li *ngFor="let post of group.posts">
{{ post.title }}
</li>
</ul>
<div>
</div>
`
})
export class ExeMessageComponent implements OnInit {
private _data$ = new BehaviorSubject<Post[]>([]);
@Input()
set data(value: Post[]) {
this._data$.next(value);
}
get data(): Post[] {
return this._data$.getValue();
}
groupPosts: GroupPosts[] = [];
ngOnInit() {
this._data$.subscribe((x: any) => {
this.groupPosts = this.groupByCategory(this.data);
});
}
ngOnDestroy(): void {
// 如果数据不断的变化需要取消订阅
this._data$.unsubscribe();
}
ngOnChanges(changes: SimpleChanges) { }
groupByCategory(data: Post[]): GroupPosts[] {
if (!data) return [];
// 去重处理
const categories = new Set(data.map(x => x.category));
// 转为二维数组
const result = Array.from(categories).map(x => ({
category: x,
posts: data.filter(post => post.category === x)
}));
return result;
}
}
image.png
-
使用Observable:
我们也可以尝试着使用Observable对象,将输入的属性的类型设置为Observable即可。
子组件message:
import { Observable, Observer } from 'rxjs';
...
@Input() data: Observable<Post[]>; // 输入属性的类型是Observable
父组件User组件:
import { Observable, Observer } from 'rxjs';
...
posts: Observable<Post[]>;
如果想在模板中直接使用的话,可以使用AsyncPipe
。
在实际的开发场景中,需要参考业务层的应用方式,如果数据源只产生一次或者很少的次数的变化,那么可以直接考虑使用*ngIf
指令,即当父组件异步数据获取到的时候才进行子组件的加载。如果是持续不断的改变,那么需要使用以上的方法去解决异步导致数据源无法获取的问题。