Angular:如何像往常一样从几个可观察的数组中收集数据?

发布于 2025-01-17 13:04:01 字数 643 浏览 3 评论 0原文

我需要做下一个 - 从后端获取数据并将其转发到子组件:

...

@Component({
  selector: 'my-component',
  template: `<my-child-component [data]="data"></my-child-component>` 
})

public data = [];

ngOnInit() {
  this.service.getParentData().subscribe(res =>  {
    res.map(r => this.service.getChildDatas(r.id)
      .subscribe(child => this.data.push(child)));
  });
}

但是在子组件中,当我尝试在 ngOnChanges 中打印数据时,我看到奇怪的数组,我可以看到元素,但长度等于 0 :

enter图像此处描述它是如何工作的以及如何实现我的目标(将数据传递给子级并像处理数组一样处理数据)?

I need to do next one - get data from backend and forward it to child component:

...

@Component({
  selector: 'my-component',
  template: `<my-child-component [data]="data"></my-child-component>` 
})

public data = [];

ngOnInit() {
  this.service.getParentData().subscribe(res =>  {
    res.map(r => this.service.getChildDatas(r.id)
      .subscribe(child => this.data.push(child)));
  });
}

But in a child component, when I try to print data in ngOnChanges, I see just strange array, I can see elements, but length is equal to 0:

enter image description here
How does it work and how can I achieve my objective (pass data to child and work with data as with array)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

电影里的梦 2025-01-24 13:04:01

您似乎有2个要管道的可观察物,一个接一个。
您需要使用 rxjs operators 实现这一目标。

实现它的方法是使用操作员

然后使用 forkjoin 操作员结果作为列表,

假设您首先收到一个数组,并且需要每个数组值通过您拥有的另一个服务来获取孩子

ngOnInit() {
  this.service.getParentData()
          .pipe(switchMap((parentData: {id: string}[]) => {
                 return forkJoin(parentData.map(r => 
                     this.service.getChildDatas(r.id))
                 })
      .subscribe((childData: any[]) => {this.data = childData});
}

You seems to have 2 observables that you want to pipe, the one after the other.
You'll need to use rxJS operators to achieve that.

The way to achieve it is to use operator switchMap, or mergeMap depending if you want the changes of the first to re-trigger the second one.

Then use forkJoin operator, to subscribe to a list of observable and threat results as a list

Assuming that you receive an array first, and need for each array value to get the child one through another service you'll have

ngOnInit() {
  this.service.getParentData()
          .pipe(switchMap((parentData: {id: string}[]) => {
                 return forkJoin(parentData.map(r => 
                     this.service.getChildDatas(r.id))
                 })
      .subscribe((childData: any[]) => {this.data = childData});
}
╰ゝ天使的微笑 2025-01-24 13:04:01

ngOnChange - 当指令的任何数据绑定属性发生更改时调用的生命周期挂钩。

这里您绑定一个数组,并且该数组被视为对象。这意味着当您发送新对象时,ngOnChange 将被触发,在我们的例子中是一个新数组(意味着新的引用 ID)。

Angular 使用相等检查运算符 === 来检测指令的输入何时发生变化。
=== 运算符检查其正在检查的对象中的引用更改。

在这里您正在更改对象的内容,而不是对象引用。

ngOnChange 将会被触发,无论是任何类型的Object,当有一个引用改变时就意味着新的Object。

所以第一个解决方案是

ngOnInit(){
this.service.getParentData().subscribe(res =>  {
    res.map(r => this.service.getChildDatas(r.id)
      .subscribe(child => {
        this.data.push(child);
        this.data = [...this.data]; // It will re assign new instance 
          data(Array) on every push
      }));
  });
} 

但我建议您在这里稍微优化一下代码,就像

ngOnInit(){
  this.service.getParentData().pipe(
    concatMap((res) => this.service.getChildDatas(res.id))
  ).subscribe({
     next: (res) => {
       this.data.push(res);
       this.data = [...this.data];
     },
  });
} 

第二个解决方案
通过将自定义对象与角度 trackBy 绑定。 Angular 将通过 trackBy 索引而不是对象引用来比较对象的相等性,这也将提高性能。

<my-child-component [data]="{trackBy:data.length,data:data}"></my-child-component>
ngOnChanges(changes: SimpleChanges): void {
   console.log(changes.data.currentValue.data);
}

更新:
你使用了.push,所以我假设你正在接收一个数据,但是如果你正在接收一个数组,那么你可以将res数组分配给this.data,通过这样做 当您

ngOnInit(){
      this.service.getParentData().pipe(
        concatMap((res) => this.service.getChildDatas(res.id))
      ).subscribe({
         next: (res) => {
           this.data = res;
         },
      });
    } 

可能要同时处理多个属性更改或希望对多个属性执行某些操作时,请使用 ngOnChanges

但如果你想处理特定的属性更改,那么更好的选择是与 @Input 一起使用 setter。

@Input() set data(data:any[]){
    console.log(data);
  };

我发现这个博客非常有帮助 使用 ngOnChanges 和 Setters 检测 Angular 中的@输入更改

ngOnChange - A lifecycle hook that is called when any data-bound property of a directive changes.

Here you are binding an array,and that is being treated as object.That means ngOnChange will be fire when you will send new Object,in our case case a new Array(means new reference id).

Angular uses the equality check operator === to detect when an input(s) to directives changes.
The === operator checks for reference changes in the objects it's checking.

And here you are changing content of Object,Not Object reference.

ngOnChange will be triggered,in case of any kind of Object,when there will be a reference change means new Object.

So 1st solution is

ngOnInit(){
this.service.getParentData().subscribe(res =>  {
    res.map(r => this.service.getChildDatas(r.id)
      .subscribe(child => {
        this.data.push(child);
        this.data = [...this.data]; // It will re assign new instance 
          data(Array) on every push
      }));
  });
} 

But I will suggest you to optimise your code little bit here like

ngOnInit(){
  this.service.getParentData().pipe(
    concatMap((res) => this.service.getChildDatas(res.id))
  ).subscribe({
     next: (res) => {
       this.data.push(res);
       this.data = [...this.data];
     },
  });
} 

2nd Solution
By binding a custom object with angular trackBy. angular will comapare that Object's equality by trackBy index rather object reference and this will improve performance also.

<my-child-component [data]="{trackBy:data.length,data:data}"></my-child-component>
ngOnChanges(changes: SimpleChanges): void {
   console.log(changes.data.currentValue.data);
}

Update:
You used .push,so I assumed that You are receiving a single data,But If you are receiving an array then you can assign res array to this.data,by doing this you are changing reference everytime

ngOnInit(){
      this.service.getParentData().pipe(
        concatMap((res) => this.service.getChildDatas(res.id))
      ).subscribe({
         next: (res) => {
           this.data = res;
         },
      });
    } 

One more Update

Use ngOnChanges when you are likely dealing with multiple properties changing at once, or wish to do something with multiple properties.

But if you want to handle specific property changes,then better option is to use setter alongside @Input.

@Input() set data(data:any[]){
    console.log(data);
  };

I found this blog very helpful Detecting @​Input changes in Angular with ngOnChanges and Setters

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文