Form control valueChanges 给出前一个值

我在表单对象 parentForm中有一个名为 'question1'的表单控件,我通过以下方式订阅了它。

它是一个单选按钮,有两个选项 YesNo,当我选择 No时,我得到 Yes,当我选择 Yes时,它是一个 No

this.parentForm.controls['question1'].valueChanges.subscribe(
(selectedValue) => {
// If option `No is selected`
console.log(selectedValue);  // displays No. OK
console.log(this.parentForm.value['question1']);  // displays Yes. Problem is here
}
);

selectedValue变量有正确的值,但如果我做 console.log(this.parentForm.value['question1'],它给出了以前的值。

我试图把一个 setTimeout()之前,从 this.parentForm.value['question1']检索值,它只是工作得很好。

setTimeout(() => {
console.log(this.parentForm.value['question1']); // gives the correct value.
}, 500);

但我的问题是,为什么 parentForm在其控件的值发生变化时不更新,而且我只是在值发生变化后才检索它的值。

注意: 我不想观察 parentForm.valueChanges,这不是我的要求。

153241 次浏览

The valueChanges event is fired after the new value is updated to the FormControl value, and before the change is bubbled up to its parent and ancestors. Therefore, you will have to access the value of the FormControl itself (which has just been patched), not a field of the FormGroup value object (which is untouched during the event).

In light of that, use this.parentForm.get('question1').value instead:

this.parentForm.controls['question1'].valueChanges.subscribe(
(selectedValue) => {
console.log(selectedValue);
console.log(this.parentForm.get('question1').value);
}
);

There is one more option, but it might not be suitable in all cases: you can wait one tick before the selectedValue is updated in FormControl:

setTimeout( () => console.log( selectedValue ) );

More on sync/async forms topic: Reactive forms in Angular Guide docs.

Try to do this

this.parentForm.controls['question1'].valueChanges.subscribe(
(selectedValue) => {
console.log(selectedValue);
console.log(this.parentForm.value.question1);
}
);

if controls of FormBuilder was updated, you can immediately recover last values from FormBuilder object through the property value

valueChanges is an Observable so you can pipe pairwise to get the previous and next values in the subscription.

// No initial value. Will emit only after second character entered
this.form.get('fieldName')
.valueChanges
.pipe(pairwise())
.subscribe(([prev, next]: [any, any]) => ... );
// Fill buffer with initial value, and it will emit immediately on value change
this.form.get('fieldName')
.valueChanges
.pipe(startWith(null), pairwise())
.subscribe(([prev, next]: [any, any]) => ... );

Example of it working in StackBlitz: https://stackblitz.com/edit/angular-reactive-forms-vhtxua

Update

If you're noticing that startWith appears to be deprecated this is not the case. There is only a single active signature for the operator, which you can read about in this answer.

Highly likely, you are using startWith(null) or startWith(undefined), they are not deprecated despite the notice, but IDE detects a wrong function signature, which is deprecated, and shows the warning.

A simple work around is providing the return type that would be expected:

// Prevent deprecation notice when using `startWith` since it has not been deprecated
this.form.get('fieldName')
.valueChanges
.pipe(startWith(null as string), pairwise())
.subscribe(([prev, next]: [any, any]) => ... );

Example of it working in StackBlitz with startWith: https://stackblitz.com/edit/angular-reactive-forms-rvxiua

Don't use:

 this.parentForm.controls['question1'].valueChanges.subscribe

Use:

this.parentForm.get('question1').valueChanges.subscribe