1

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?

2 Answers 2

3

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()

Sign up to request clarification or add additional context in comments.

Comments

0

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).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.