【问题标题】:How can I share a constantly changing array between Parent and Child Component?如何在父组件和子组件之间共享不断变化的数组?
【发布时间】:2021-12-23 10:02:26
【问题描述】:

我有两个组件,一个父组件和一个子组件。在父组件中,我有一个空数组:

publicCrowsOnPerchValues: number[] = [];

我使用这个数组在使用chart.js 的图表上显示数据。我正在使用 Firebase 的 RTDB 来存储和检索数据。以下是上述数组在父组件中的填充方式:

 getCrowOnPerchDataChildren() {
    //get a snapshot of the child added
    this.$childCrowOnPerchSub = this.crowboxService
    .getCrowOnPerchData()
    .stateChanges();

    this.$childCrowOnPerchSub
    .subscribe(action => {
      //set the showUserId to false as the user has already set up the crowbox
      this.showUserId = false;
      //get the index of the key from the date array
      let indexOfKey = this.crowsOnPerchDate.indexOf(action.key);
      //if the index is -1, then the date does not currently exist
      //this means that it is a new date, so we push it onto the array
      //since it is a new date, we also push on the value onto the value
      //array
      if (indexOfKey == -1) {
        this.crowsOnPerchDate.push(action.key);
        this.crownsOnPerchValues.push(action.payload.val().value);
      } else {
        //if it does exist, then we don't need to add the new date
        //simply replace the existing data value with the new data value
        //for the same date 
        this.crownsOnPerchValues[indexOfKey] = action.payload.val().value;
      }

      //reset the bar charts data as well as labels
      this.crowOnPerchChartLabels = this.crowsOnPerchDate;
      this.crowOnPerchChartData = [
        { data: this.crownsOnPerchValues, label: "Number Of Crows That Landed On The Perch" }
      ];
    });
  }

在上述函数中,我实质上是在用户将数据添加到 Firebase RTDB 时填充数组。它通过订阅 observable 起作用。

现在,我想将此数组传递给我的子组件。 在子组件中,我设置了一个Input() 变量,如下所示:

  @Input() crowsOnPerch!:number[];

然后,在 parent.component.html 中,我像这样传递数组:

    <app-child [crowsOnPerch]="crownsOnPerchValues"></app-child>

这可行,但我收到的是空数组。我怎样才能在更新时不断收到阵列?我应该改用共享服务吗?

【问题讨论】:

  • 只是猜测:可能是异步管道

标签: angular angular-components


【解决方案1】:

角度检测策略的工作方式是,对于复杂的数据类型,如 array 或 object,它不会检查内部的内容,它只会检查这些数据类型的身份。

这意味着如果您想让更改反映在您的 @Input 中,则不应更改现有数组,而应创建一个新数组(因此您的数组的身份将被更改)。

您可以使用扩展运算符来做到这一点:

// Instead of this:
this.crownsOnPerchValues.push(action.payload.val().value);

// Do this:
this.crownsOnPerchValues = [
     ...this.crownsOnPerchValues,
     action.payload.val().value
]


// Instead of this:
this.crownsOnPerchValues[indexOfKey] = action.payload.val().value;

// Do this: 
this.crownsOnPerchValues[indexOfKey] = action.payload.val().value;
this.crownsOnPerchValues = [...this.crownsOnPerchValues];

【讨论】:

  • 这真的很有趣。这也很有意义,但是在实现它后,我仍然在子组件中得到一个未定义/空数组。进行更改后,图表和数据等都在父组件中工作。
  • 如果你也能分享你的子组件的代码就好了
  • 很抱歉,子组件现在是裸露的。我唯一要做的就是在ngOnInit() 中使用console.log(crowsOnPerch) 来测试数组的输出。
  • 这可能是个问题。 ngOnInit() 只在最开始时触发一次,当您的数组为空时。尝试将控制台日志放入ngOnChanges(),或者只是通过for循环在子组件的模板中打印值。
  • 啊,当然!我忘记了ngOnInit() 的工作原理。 OnChanges 成功了,谢谢!
【解决方案2】:

“反应式”的做法是让您的数组成为一个 Observable 流,然后连接子组件以将其作为输入接收。它应该看起来像这样,

父组件

*.ts

public myArray$: BehaviorSubject<number[]> = new BehaviorSubject<number[]>([]);

当您的数组发生变化时,将该更改推送到 Observable 流

this.myArray$.next([1, 2, 3]);

*.html

<child-component [myArrayInput]="myArray$ | async">

子组件

*.ts

@Input() myArray: number[];

我使用BehaviorSubject 来允许使用 .next() 函数,但您也可以使用简单的 Observable 来做到这一点。

它无法按照您的方式工作的原因是 Angular 的变更检测。它不会检测到数组内部的变化,只会检测整个引用的变化。每次发生更改时,您都必须创建一个全新的数组并将其分配给您传递给子组件的变量。

【讨论】:

  • 将 observable 作为@Input 值传递通常是一种不好的做法。你应该尽量让你的子组件保持愚蠢。
  • 公平点,我将从答案中删除该位。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-03
  • 2021-03-02
  • 2016-06-10
  • 2016-04-27
相关资源
最近更新 更多