Form control valueChanges gives the previous value
AngularAngular2 FormsAngular2 FormbuilderAngular Problem Overview
I have a form control with name 'question1'
within the form object parentForm
and I have subscribed to it in the following way.
Its a radio button with two options Yes
and No
, when I select No
I get Yes
and when I select Yes
its a 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
variable has the correct value but if I do console.log(this.parentForm.value['question1']
it gives the previous value.
I tried to put a setTimeout()
before retrieving value from this.parentForm.value['question1']
, It just works fine.
setTimeout(() => {
console.log(this.parentForm.value['question1']); // gives the correct value.
}, 500);
But my question is why parentForm
is not updated when its control's value changes and that too I am retrieving its value only after value was changed.
Note: I don't want to observe for parentForm.valueChanges
, not my requirement.
Angular Solutions
Solution 1 - Angular
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
Solution 2 - Angular
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);
}
);
Solution 3 - Angular
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
Solution 4 - Angular
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][2] in Angular Guide docs.
[2]: https://angular.io/guide/reactive-forms#async-vs-sync "Reactive forms in Angular Guide"