如何正确使用 Angular 中的 Observable 将数组数据从父级发送到子级?
Posted
技术标签:
【中文标题】如何正确使用 Angular 中的 Observable 将数组数据从父级发送到子级?【英文标题】:How to send array data from Parent to Child using Observable in Angular properly? 【发布时间】:2021-09-04 20:18:02 【问题描述】:首先,我需要从父组件向子组件发送一些 json 数据,但是我在 ngOnInit 事件中从 http 请求加载数据,当我以正常方式提交数据时,数据不会发送到我的子组件,这里这是我的第一次尝试:
data: TransSalesOrder = new TransSalesOrder();
ngOnInit(): void
if (this.dataId > 0)
const data: SelectItem = new SelectItem();
data.id = this.dataId;
const sb = this.transSalesOrderService.view(data).subscribe(response =>
this.transSalesOrderForm.patchValue(response);
this.data = response;
if (this.transSalesOrderForm.get('isDeleted').value)
this.editButton = false;
this.deleteButton = false;
this.isDeleted = true;
, err =>
this.openSnackBar(err);
);
this.subscriptions.push(sb);
然后在我的父 html 中,我发送了这样的数据:
<app-trans-sales-order-detail-list [salesOrderDetailsInput]="data.transSalesOrderDetail"></app-trans-sales-order-detail-list>
然后当我 console.log 进入子 component.ts 时:
@Input() salesOrderDetailsInput: any;
constructor()
ngOnInit(): void
console.log(this.salesOrderDetailsInput);
打印出'未定义'
那我觉得parent来不及加载数据了,这里是我第二次尝试,我需要ngOnChanges事件:
ngOnChanges(changes: SimpleChanges)
console.log(changes.salesOrderDetailsInput.currentValue);
然后我可以获取从父级发送的数据,但是我在 *** 的某个地方读到,有人说需要从父级值添加异步函数,并且子组件中的 @Input 应该检索为 Observable,然后我假设如果我可以做到这一点,我将不再需要 ngOnChanges,这是我的尝试:
在我的父代码中:
<app-trans-sales-order-detail-list [salesOrderDetailsInput]="data.transSalesOrderDetail | async"></app-trans-sales-order-detail-list>
但是在我的 Intellij Idea ide 中编译时它给了我错误 ERROR Error: InvalidPipeArgument: '' for pipe 'AsyncPipe'
和错误 Argument type TransSalesOrderDetail[] is not assignable to parameter type Observable<unknown> | Promise<unknown>
这是我的子组件.ts:
@Input() salesOrderDetailsInput: Observable<any>;
我错过了什么?
【问题讨论】:
有transSalesOrderDetail
的类型吗?您应该将其用作子组件输入的类型 - 如下所示:@Input() salesOrderDetailsInput: TransSalesOrderDetail;
【参考方案1】:
成员变量this.data
被异步赋值并在绑定[salesOrderDetailsInput]="data.transSalesOrderDetail"
执行时持有一个空对象。所以它会发送transSalesOrderDetail
的当前状态,即undefined
。
IMO 不需要在这里使用async
。你可以试试下面的
选项 1:*ngIf
使用*ngIf
有条件地渲染子组件标签
<app-trans-sales-order-detail-list
*ngIf="!!data && !!data.transSalesOrderDetail"
[salesOrderDetailsInput]="data.transSalesOrderDetail"
></app-trans-sales-order-detail-list>
这里没有定义transSalesOrderDetail
属性时子组件不会被渲染。有关双 bang !!
运算符的更多信息,请参阅 here。
选项2:安全导航操作员?.
访问属性时使用安全导航运算符?.
。它会在尝试访问之前检查该属性是否在对象中定义。
<app-trans-sales-order-detail-list
[salesOrderDetailsInput]="data?.transSalesOrderDetail"
></app-trans-sales-order-detail-list>
这里可能会渲染子组件,但没有绑定到transSalesOrderDetail
。
话虽如此,我可以提供一些额外的建议
当使用ngOnChanges
挂钩读取@Input
属性的更改时,最好在尝试访问之前检查该属性是否已定义。在具有单个 @Input
属性的组件中,这可能不是问题。但是当有多个@Input
s 时,可能会发生一些已定义而有些未定义的情况。
@Input() salesOrderDetailsInput: any;
ngOnChanges(changes: SimpleChanges)
if (!!changes &&
!!changes.salesOrderDetailsInput &&
!!changes.salesOrderDetailsInput.currentValue
)
console.log(changes.salesOrderDetailsInput.currentValue);
【讨论】:
【参考方案2】:您必须使 transSalesOrderDetail
在您的父母中成为可观察的。然后在你的孩子中订阅这个 observable。
<app-trans-sales-order-detail-list [salesOrderDetailsInput]="data.transSalesOrderDetail"></app-trans-sales-order-detail-list>
那么在你的孩子身上,
@Input() salesOrderDetailsInput: any;
...
...
ngOnInit(): void
salesOrderDetailsInput.subscribe(data => console.log(data));
您可以选择使用async
管道,这将消除订阅的必要性 - 但您必须使用 onChange 事件来获取最新数据。,
【讨论】:
【参考方案3】:当您调用 API 或其他东西以异步获取数据时,异步管道实际上就像订阅 observable 一样。当您使用async
管道时,|
的左侧应该是observable
。示例:
parent.component.html
<ChildComponent [inputData] = "_data | async"></ChildComponent>
parent.component.ts
public data = new BehaviorSubject(null);
public _data = this.selectedSubmission.asObservable();
ngOnInit():
this.http.get(url).subscribe((respData:any)=>
this.data.next(respData);
)
child.component.ts
@Input() inputData: any;
ngOnChanges(changes: SimpleChanges)
console.log(this.inputData);
注意:您应该使用服务文件来编写所有 API 调用。上面的例子演示了应该如何做。
Live Demo
【讨论】:
【参考方案4】:我认为 Input 无法正常工作的原因是,data.transSalesOrderDetail
会导致它的第一次评估出错,所以尝试添加Elvis operator。一旦ngOnInit()
中的服务执行完毕,数据将按预期正确加载
另一种方法是,定义变量并将其添加到输入绑定..
在component.ts
中transSalesOrderDetail: TransSalesOrderDetail;
...
this.transSalesOrderForm.patchValue(response);
this.data = response;
this.transSalesOrderDetail = response.transSalesOrderDetail;
...
在 HTML 中:
<app-trans-sales-order-detail-list [salesOrderDetailsInput]="data?.transSalesOrderDetail"></app-trans-sales-order-detail-list>
// or with new variable <app-trans-sales-order-detail-list [salesOrderDetailsInput]="transSalesOrderDetail"></app-trans-sales-order-detail-list>
最好也为 Input 添加类型
@Input() salesOrderDetailsInput: TransSalesOrderDetail;
【讨论】:
以上是关于如何正确使用 Angular 中的 Observable 将数组数据从父级发送到子级?的主要内容,如果未能解决你的问题,请参考以下文章
在 RxJS Observable 中“展平”数组的最佳方法