Understanding Angular FormBuilder with Groups and ngModel data and how/if they can be used together?
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?
Solution 1:
Zerospi, the problem is that you create the form before you has data in your variable "item" - this is the reason you don't see the value
-
NEVER Mix together Reactive forms and Template-Driven (ngModel)
The use
[(ngModel)]="variable" [ngModelOptions]="{standalone: true}
it's ONLY to get an input that not belong to the FormGroup.e.g you can has a "checkbox" to show a new input. Some like
movil:boolean=false; form=new FormGroup({ phone:new FormControl() mobile:new FormControl() }) <form [formGroup]="form"> Phone<input formControlName="phone"> <input type="checkbox" [(ngModel)]="movil" [ngModelOptions]="{standalone:true}">Mobile <input *ngIf="movil" formControlName="mobile"> </form>
Well, really we use
<input type="checkbox" [ngModel]="movil" (ngModelChange)="movil=$event;!$event && form.get('mobile').setValue('') [ngModelOptions]="{standalone:true}">Mobile
To "clean" the FormControl "mobile" if we uncheck
How control a FormGroup?
-
In general we can have a function in the way
getFromGroup(data:any=null) { data=data || {phone:'',mobile:''} return this.fb.group({ phone:[data.phone,Validators.required] mobile:data.mobile } }
And use
this.form=this.getFormGroup(this.item) //if we have an object "this.item" this.form=this.getFormGroup() //if we want an empty Form
-
Another approach is create the form an use patchValue()
this.form=this.fb.group({ phone:['',Validators.required] mobile:'' } this.form.patchValue(this.item)
-
To avoid initial errors (e.g. we create the form after a call to an API), sometimes is util use some like
<form *ngIf="form" [formGroup]="form"> ... </form>
-
Remember that a FormControl is disabled, don't showed in form.value, you need use form.getRawValue()