【问题标题】:Angular async pipe to Input property in child component not being set未设置子组件中输入属性的角度异步管道
【发布时间】:2019-02-01 17:17:26
【问题描述】:

似乎子组件仅在 Observable 第一次发射时才被设置。

容器组件(为调试添加了map操作符):

this.form$ = this.createOrderStore.pipe(select(getFormGroup)).pipe(
  tap((val) => {
    console.log('observable emitted');
    console.log(val);
    return val;
  })
);

子组件:

private _form: FormGroup;
@Input()
set form(val) {
  console.log('set form');
  if (val) {
    this._form = val;
  }
}
get form() {
  return this._form;
}

容器模板:

<app-order-form [form]="form$ | async"></app-order-form>

observable 第一次发射时,控制台显示“observable emit”,val 未定义,然后是“set form”。但是,由于第一个值是未定义的,我们还没有在模板中设置它。

observable 第二次发出,控制台记录'observable emit',val 是一个完整的表单组(因此它应该触发更改检测),但没有记录'set form'。这第二个值是我实际需要在模板中设置的值。

只有在 observable 第一次发射时才设置子组件属性有什么原因吗?

【问题讨论】:

  • 您实际上不会多次设置表单;当 Observable 发出时,它不会设置表单。您应该在子模板中使用异步管道
  • 它应该会触发。你确定第二个包含一些东西,因为角度检查是否应该更新和undefined === undefined,所以它不会进入设置函数?尝试将其记录在 observable 中(顺便说一句,您可以使用 tap 进行记录,无需从中返回任何内容)
  • @PierreDuc 我按照您的建议做了并编辑了问题。肯定第二次才是真正的价值
  • 不知道为什么它不起作用。但是如果你只需要第二个,你可以在form$ obs 中添加一个filter.pipe(filter((form) =&gt; !!form))。不过还是没解释
  • @PierreDuc 有趣的是,我按照您的建议添加了过滤器运算符,并且第一次仍然记录了“设置表单”,就像过滤器没有阻止发出值一样,即使表单是绝对未定义。我真的被这里吹走了......

标签: javascript angular typescript rxjs ngrx


【解决方案1】:

我相信您的问题仅在您拥有 ChangeDetectionStrategyOnPush 时才会发生。由于getFormGroup 方法,您可能在ngAfterViewInit 挂钩中设置了this.form$,对吗?有两种方法可以解决这个问题。

简单的方法,使用ChangeDetectorRef

容器组件:

constructor(readonly cd: ChangeDetectorRef) {}

ngAfterViewInit(): void {
   // ...

   this.form$ = this.createOrderStore.pipe(select(getFormGroup));
   this.cd.markForCheck();
}

另一种方法是使用Subject 来更新formGroup,并在类构造中使用已定义forms$

formGroup$ = new ReplaySubject<FormGroup>(1);

readonly form$ = this.formGroup$.pipe(
  mergeMap((formGroup) => this.createOrderStore.pipe(
    select(formGroup)
  ))
);

ngAfterViewInit(): void {
   // ...
   this.formGroup$.next(getFormGroup);
}

只是为了澄清目前代码中发生的情况是容器组件上的form$ 属性未定义。那是您正在登录子组件的未定义,并且因为您在 ngAfterViewInit 挂钩内设置了 form$,它不会被更改检测器拾取,除非您明确告诉它进行检查

【讨论】:

  • 更改检测策略是默认的。我真的不想为应该按照我的方式工作的东西做这一切......我现在正在做一个堆栈闪电战,希望我能重现它,因为我开始有这样的感觉可能是一个错误
  • @SeanIvins 我已经有一段时间没有在没有 OnPush 的情况下使用 angular 了,所以同样的问题可能仍然存在,只是因为你将它设置在生命周期钩子中。但是如果你不使用 OnPush,我只能想象将它包装在 setTimeout(() =&gt; this.form$ = this.createOrderStore.pipe(select(getFormGroup))) 中也可以
  • @SeanIvins 我检查了文档,是的,在ngAfterViewInit 中设置一个属性将需要第二个更改检测周期来获取
  • 我在构造函数中设置属性。我创建了一个堆栈闪电战,基本上就是我正在做的事情,它可以工作......stackblitz.com/edit/angular-ccli97我希望我能在我的应用程序中看到我做错了什么
  • 你确定你没有错字forms$ | async vs form$ | async
猜你喜欢
  • 2016-09-05
  • 1970-01-01
  • 2021-02-02
  • 1970-01-01
  • 2019-08-09
  • 2020-10-22
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
相关资源
最近更新 更多