Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I bind a form to a model in Angular 6 using reactive forms?

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?

like image 582
zer0 Avatar asked Jul 14 '18 19:07

zer0


People also ask

Is data binding done in Reactive forms?

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.

Should I use Reactive forms or template forms?

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.


2 Answers

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.

like image 35
GreyBeardedGeek Avatar answered Sep 18 '22 14:09

GreyBeardedGeek


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.

Binding to the form

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 }} 

Creating the 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();     } 

Filling your form with data

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);             });     } 

Getting data out of the form

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.

like image 123
amphetamachine Avatar answered Sep 19 '22 14:09

amphetamachine