Prior to angular 6, I was using [(ngModel)]
to directly bind my form field to the model. This is now deprecated (can't use with reactive forms) and I am not sure how to update my model with the form values. I could use form.getRawValue()
but this would require me to replace my current model with the new rawValue - this is not favourable since my main model is larger and has more fields than the local form model.
Any ideas?
Don't use [(ngModel)]
! Reactive forms are much nicer. They make manual ngModel
bindings obsolete, and they have some pretty sweet built-in features that only a couple of which I'm going to cover in this answer.
If you're binding to a form control such as a text input, use this template syntax:
<ng-container [formGroup]="this.myFormGroup">
<input type="text" formControlName="field1">
<input type="text" formControlName="field2">
<ng-container formGroupName="subgroupName">
<input type="text" formControlName="subfield2">
</ng-container>
<input type="text" formControlName="myRequiredField">
</ng-container>
(field1
, field2
, subgroupName
, subfield2
, and myRequiredField
are all arbitrary control and control group names that correspond to parts of your form, see below when creating the FormGroup
object.)
Read-only data bindings to the model of the FormGroup
are accessed a little differently in your template:
{{ this.myFormGroup.get('field1').value }}
{{ this.myFormGroup.get('subgroupName.subfield2').value }}
<!-- Hint: use an array if you have field names that contain "." -->
{{ this.myFormGroup.get(['subgroupName', 'subfield2']).value }}
FormGroup
In your component class, in constructor()
(this should be before the template renders), use the following syntax to build a form group to talk to this form:
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
...
public readonly myFormGroup: FormGroup;
...
constructor(private readonly formBuilder: FormBuilder) {
this.myFormGroup = this.formBuilder.group({
field1: [],
field2: [],
subgroupName: this.formBuilder.group({
subfield2: [],
}),
myRequiredField: ['', Validators.required],
});
this.retrieveData();
}
If your component needs to retrieve data from a service as it loads, you must make sure it starts the transfer after the form has been built, then use patchValue()
to put the data from your object into the FormGroup
:
private retrieveData(): void {
this.dataService.getData()
.subscribe((res: SomeDataStructure) => {
// Assuming res has a structure like:
// res = {
// field1: "some-string",
// field2: "other-string",
// subgroupName: {
// subfield2: "another-string"
// },
// }
// Values in res that don't line up to the form structure
// are discarded. You can also pass in your own object you
// construct ad-hoc.
this.myFormGroup.patchValue(res);
});
}
Now, say your user clicks submit and now you need to get the data back out of your form and POST
it back to your API thru a service. Just use getRawValue
:
public onClickSubmit(): void {
if (this.myFormGroup.invalid) {
// stop here if it's invalid
alert('Invalid input');
return;
}
this.myDataService.submitUpdate(this.myFormGroup.getRawValue())
.subscribe((): void => {
alert('Saved!');
});
}
All these techniques eliminate the need for any [(ngModel)]
bindings, since the form maintains its own internal model inside the FormGroup
object.