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

Issue

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

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

  1. 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?

  2. 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
    
  3. Another approach is create the form an use patchValue()

    this.form=this.fb.group({
       phone:['',Validators.required]
       mobile:''
    }
    this.form.patchValue(this.item)
    
  4. 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>
    
  5. Remember that a FormControl is disabled, don’t showed in form.value, you need use form.getRawValue()

Answered By – Eliseo

Answer Checked By – Cary Denson (AngularFixing Admin)

Leave a Reply

Your email address will not be published.