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?
We can perform Two way binding in Template driven forms but there is no two way binding in Reactive forms. Angular provides the methods to update the values from the component class. Reactive forms are used on complex cases, like dynamic forms element, dynamic validations etc.
Template Driven Forms are based only on template directives, while Reactive forms are defined programmatically at the level of the component class. Reactive Forms are a better default choice for new applications, as they are more powerful and easier to use.
As explained more fully in the Angular Documentation, with reactive forms, you do not bind the form directly to your model. Rather, you use a FormBuilder to build a FormGroup object (essentially, "the form") that will maintain it's own model. During construction, you have the opportunity to set initial values in the form, which you would usually do from your model.
You then bind form controls in your template to the form's model. User interaction with the form controls updates the form's model.
When you are ready to do something with the form data (e.g. "submit" the form), you can get the values from the form fields by using either the FormGroup's value property or it's getRawValue() method - these two behave differently, see the documentation for details.
Once you get the values from the form, if you wish, you can update your model with the values from the form.
Don't use [(ngModel)]
! Reactive forms are much nicer. They make manual ngModel
bindings obsolete, and they have some pretty sweet built-in features 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With