Home > Mobile >  Understanding Angular FormBuilder with Groups and ngModel data and how/if they can be used together?
Understanding Angular FormBuilder with Groups and ngModel data and how/if they can be used together?

Time:01-13

I am running into some issues with trying to use FormBuilder in my Angular app and how I can set the default values in the form based on my ngModel data.

In my class, I have the following code:

form: FormGroup;

constructor(
    private fb: FormBuilder,
) { }

ngOnInit() {

    this.form = this.fb.group({
        category: ['', [Validators.required]],
        quantity: [1, [Validators.required]],
        size: ['', [Validators.required]],
        title: ['', [Validators.required]],
    });
}

When I look at the {{ form.value | json }} in my template, it shows the original values with the empty values. So I decided to try to set the default values in the FormBuilder group like this:

    this.form = this.fb.group({
        category: [this.item.category, [Validators.required]],
        quantity: [this.item.quantity, [Validators.required]],
        size: [this.item.size, [Validators.required]],
        title: [this.item.title, [Validators.required]],
    });

But I am given these errors:

ERROR Error: formGroup expects a FormGroup instance. 
ERROR TypeError: Cannot read properties of undefined (reading 'category')
ERROR TypeError: Cannot read properties of undefined (reading 'value')

This is my template for the form:

<form [formGroup]="form">
        <ion-item lines="none">
            <ion-label position="stacked">Category</ion-label>
            <ion-select [(ngModel)]="item.category" [ngModelOptions]="{standalone: true}">
                <ion-select-option *ngFor="let category of categories" [value]="category">
                    {{ category }}
                </ion-select-option>
            </ion-select>
        </ion-item>

        <ion-item lines="none">
            <ion-label position="stacked">Title</ion-label>
            <ion-input [(ngModel)]="item.title" [ngModelOptions]="{standalone: true}"></ion-input>
        </ion-item>

        <ion-item lines="none">
            <ion-label position="stacked">Size</ion-label>
            <ion-input [(ngModel)]="item.size" [ngModelOptions]="{standalone: true}"></ion-input>
        </ion-item>

        <ion-item lines="none">
            <ion-label position="stacked">Quantity</ion-label>
            <ion-input type="number" [(ngModel)]="item.quantity" [ngModelOptions]="{standalone: true}"></ion-input>
        </ion-item>

    </form>

Any thoughts what is wrong with my approach and how I can get the default values from my this.item to appear in the form?

CodePudding user response:

There are 2 approaches regarding forms: Template driven (more logic in .html and Reactive (more logic and control in .ts). With formBuilder injected and [formGroup]="form" you went for reactive approach which is more spread among experienced devs and tends to be more comfortable since it gives more control over your forms.

It is not a good practice to use two-way data binding with this approach. You should put formControlName="controlNameXYZ" to the according input field and do that for all your input fields defined when building form.

Don't forget to have ReactiveFormsModule inside imports array of your module (or AppModule if you only have one in whole application).

CodePudding user response:

Mixing together Reactive forms and Template-Driven (ngModel) is not a good practice.

If you already have the form and your HTML form has its <form [formGroup]="form">, in order to use it you just have to use in your HTML formControlname. For instance, for the size field, would be something like this:

<ion-input formControlName="size" "></ion-input>

And, when you want to get its value in your code, you only have to do:

const myValue = this.form.value?.size;
// or, maybe better way:
const myValue2 = this.form.get('size').value;

IMPORTANT: In order to use reactive or template form, don't forget to impor its modules in your main module (usually in app-routing.module.ts) or into the module you want to use them.

import {FormsModule, ReactiveFormsModule} from '@angular/forms';

@NgModule({  
imports: [  
 // other imports ... 
FormsModule, // for template-driven 
ReactiveFormsModule,  // for Reactive
 ],  
})  
export class AppModule { }






  •  Tags:  
  • Related