Javascript 未设置子组件中输入属性的角度异步管道

Javascript 未设置子组件中输入属性的角度异步管道,javascript,angular,typescript,rxjs,ngrx,Javascript,Angular,Typescript,Rxjs,Ngrx,子组件似乎只有在可观察对象第一次发射时才被设置 容器组件(为调试而添加的映射运算符): 子组件: private _form: FormGroup; @Input() set form(val) { console.log('set form'); if (val) { this._form = val; } } get form() { return this._form; } constructor(readonly cd: ChangeDetectorRef) {

子组件似乎只有在可观察对象第一次发射时才被设置

容器组件(为调试而添加的映射运算符):

子组件:

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

ngAfterViewInit(): void {
   // ...

   this.form$ = this.createOrderStore.pipe(select(getFormGroup));
   this.cd.markForCheck();
}
容器模板:

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

第一次可观测发射时,控制台显示“可观测发射”,val未定义,然后显示“设置形式”。但是,由于第一个值是未定义的,所以我们还没有在模板中设置它

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


子组件属性仅在可观察对象第一次发射时设置,这有什么原因吗?

我相信只有当您将
ChangeDetectionStrategy
更改为
OnPush
时,问题才会发生。您正在设置
this.form$
可能是在
ngAfterViewInit
钩子中,因为
getFormGroup
方法,对吗?有两种方法可以解决这个问题

使用
ChangeDetectorRef

容器组件:

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

ngAfterViewInit(): void {
   // ...

   this.form$ = this.createOrderStore.pipe(select(getFormGroup));
   this.cd.markForCheck();
}
另一种方法是使用
主题
,来更新表单组,并使用在类构造时已经定义了
表单$

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

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

ngAfterViewInit(): void {
   // ...
   this.formGroup$.next(getFormGroup);
}
formGroup$=新的ReplaySubject(1);
只读表单$=此.formGroup$.pipe(
mergeMap((formGroup)=>this.createOrderStore.pipe(
选择(formGroup)
))
);
ngAfterViewInit():void{
// ...
this.formGroup$.next(getFormGroup);
}

只需澄清一下代码中目前发生的情况,即容器组件上的
form$
属性未定义。这是您在子组件中登录的未定义组件,因为您正在
ngAfterViewInit
钩子中设置
表单$
,所以更改检测器不会拾取它,除非你特别告诉它去检查,否则我相信你的问题只会在你有
将检测策略
更改为
OnPush
时发生。您正在设置
this.form$
可能是在
ngAfterViewInit
钩子中,因为
getFormGroup
方法,对吗?有两种方法可以解决这个问题

使用
ChangeDetectorRef

容器组件:

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

ngAfterViewInit(): void {
   // ...

   this.form$ = this.createOrderStore.pipe(select(getFormGroup));
   this.cd.markForCheck();
}
另一种方法是使用
主题
,来更新表单组,并使用在类构造时已经定义了
表单$

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

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

ngAfterViewInit(): void {
   // ...
   this.formGroup$.next(getFormGroup);
}
formGroup$=新的ReplaySubject(1);
只读表单$=此.formGroup$.pipe(
mergeMap((formGroup)=>this.createOrderStore.pipe(
选择(formGroup)
))
);
ngAfterViewInit():void{
// ...
this.formGroup$.next(getFormGroup);
}

只需澄清一下代码中目前发生的情况,即容器组件上的
form$
属性未定义。这是您在子组件中登录的未定义组件,因为您正在
ngAfterViewInit
钩子中设置
表单$
,所以它不会被更改检测器拾取,除非您特别告诉它去检查

,否则实际上不会多次设置表单;当可见光发射时,它不会设置形式。您应该在子模板中使用异步管道,它应该触发。因为angular通过引用检查是否应该更新并且
undefined===undefined
,所以它不会进入set函数,所以您确定第二个函数包含某些内容吗?试着把它记录在可观察的日志中(顺便说一句,你可以使用
点击
,不需要返回任何内容)@PierreDuc我按照你的建议做了,编辑了这个问题。当然,第二次是真正的价值,我不知道为什么它不起作用。但是如果您只需要第二个过滤器,您可以向
表单$
obs:
.pipe(filter((form)=>!!form))
添加一个
过滤器。还是无法解释though@PierreDuc非常有趣的是,我像你建议的那样添加了filter操作符,“set form”仍然是第一次被记录的,就像filter没有阻止值被释放一样,即使form确实是未定义的。我真的被吹到这里了……你实际上不会多次设置表单;当可见光发射时,它不会设置形式。您应该在子模板中使用异步管道,它应该触发。因为angular通过引用检查是否应该更新并且
undefined===undefined
,所以它不会进入set函数,所以您确定第二个函数包含某些内容吗?试着把它记录在可观察的日志中(顺便说一句,你可以使用
点击
,不需要返回任何内容)@PierreDuc我按照你的建议做了,编辑了这个问题。当然,第二次是真正的价值,我不知道为什么它不起作用。但是如果您只需要第二个过滤器,您可以向
表单$
obs:
.pipe(filter((form)=>!!form))
添加一个
过滤器。还是无法解释though@PierreDuc非常有趣的是,我像你建议的那样添加了filter操作符,“set form”仍然是第一次被记录的,就像filter没有阻止值被释放一样,即使form确实是未定义的。我在这里真的被吓坏了…更改检测策略是默认的。我真的不想为了我的工作方式而做这些。。。我现在正在做一个stackblitz,希望我能复制它,因为我开始觉得这可能是一个bug@SeanIvins我已经有一段时间没有使用angular without OnPush了,所以可能同样的问题仍然存在,只是因为您将它设置在生命周期挂钩中。但是如果不使用OnPush,我只能想象将它包装在
setTimeout(()=>this.form$=this.createOrderStore.pipe(select(getFormGroup))
中也会work@SeanIvins我查过文件了是的settin