角度表单控件值更改不会通知

时间:2018-11-04 19:57:46

标签: angular typescript rxjs

我有一个基本的表单控件,订阅了valueChanges。

@Component({
  selector: 'my-app',
  template: `
       <input [formControl]="control" />
       <div>{{ name$ | async | json }}</div>
   `
})
export class AppComponent implements OnInit {
  name$: Observable<string>;
  control;

  constructor(private builder: FormBuilder) {
    this.control = this.builder.control('');
  }

  ngOnInit() {
    this.name$ = this.control.valueChanges
      .pipe(
        map((name) => {
          console.log('fired', name)
          return name;
        })
      );


    this.control.setValue('2');
  }
}

当我调用setValue时,观察者没有收到任何通知。似乎异步管道不起作用。如果我将setValue包装在setTimeout中,则可以正常工作。为什么会这样呢?

我还试图添加shareReplay(),该方法也无效。

4 个答案:

答案 0 :(得分:0)

这个问题的解决方案

this.control.setValue('2'); // I move any changes at the top for startWith operater 
this.name$ = this.control.valueChanges
  .pipe(
    startWith(this.control.value),
    map((name) => {
      console.log('fired', name)
      return name;
    })
  );

另一种无需使用async运算符即可工作的方法,这意味着我已在控制值更改为“ 2”之前订阅了值更改,并且稍后已捕获所有更改

this.control.valueChanges
  .pipe(
    map((name) => {
      console.log('fired', name)
      return name;
    })
  )
  .subscribe ( v => this.value = v )

模板

{{value}}

为什么问题中带有async的示例不起作用,异步操作符将订阅可观察的对象,但是在异步订阅时,控件的当前值将为'2',因此没有更改为何第一个值将为不会作为表单控件的更改发出,可观察到的另一件事是懒惰的,因此当您订阅时,您将获得该值,任何更改之前都不会被看到

答案 1 :(得分:0)

如果你想在 Angular 中观察一个 from 控制值的通用解决方案:

this.value$ = defer(() => {
  return this.control.valueChanges.pipe(startWith(this.control.value));
});

通过使用 defer,您可以确保当您的 observable 被订阅时,您的 startWith 值将是您表单的实际值。如果您没有使用 defer,您的 startWith 值将只是您的 observable 被创建(未订阅)时表单的值。

如果你想在你的应用程序中重用它,你可以将该逻辑放在一个 utils 函数中:)

// utils.ts
export function observeControlValue$(formControl: AbstractControl): Observable<any> {
    return defer(() => {
        return formControl.valueChanges.pipe(startWith(formControl.value));
    });
}

// component.ts
this.value$ = observeControlValue$(this.control);

答案 2 :(得分:-1)

当您从ngOnInit调用视图时,尚未初始化视图的原因(HTML标记)尚未初始化。 您需要将其添加到ngAfterViewInit()

import AfterViewInit from '@angular/core';
export class AppComponent implements AfterViewInit{ 
   ngAfterViewInit() {
    this.name$ = this.control.valueChanges
      .pipe(
        map((name) => {
          console.log('fired', name)
          return name;
        })
      );


    this.control.setValue('2');
  }
}

尽管您需要进行测试,但我没有

答案 3 :(得分:-1)

如果要使用反应形式,则应使用FormControl而不是FormBuilder。

您的控件应该这样初始化:

export class AppComponent implements OnInit {
public control = new FormControl('');

HTML为:<input type="text" [formControl]="control">

然后,您无需使用Observable<string>即可

<div>{{ control.value | json }}</div>

有关详细信息,请参见ReactiveForm文档:https://angular.io/guide/reactive-forms