How to initialize form group in Angular 4?

Burak picture Burak · Oct 16, 2017 · Viewed 35.1k times · Source

I have the following ngOnInit method:

ngOnInit() {
    this.countries = this.sharedService.getCountries();
    this.shopService.getCurrentShopFromCache().then(shop => {
        this.shop = shop;
        this.myFormGroup = this.fb.group({
            name: [this.shop.name[this.shop.defaultLanguage.code], [Validators.required]],
            address: [this.shop.address.address],
            city: [this.shop.address.city],
            state: [this.shop.address.state],
            country: [this.shop.address.country, [Validators.required]],
            phone: [this.shop.phone],
            email: [this.shop.email],
            website: [this.shop.social.website],
            twitter: [this.shop.social.twitter],
            facebook: [this.shop.social.facebook],
            instagram: [this.shop.social.instagram],
            foursquare: [this.shop.social.foursquare]
        });
    }
    );
}

I'm getting

formGroup expects a FormGroup instance. Please pass one in.

Where am I wrong?

UPDATE:

 <form *ngIf="shop" class="m-form m-form--fit m-form--label-align-right" [formGroup]="myFormGroup" novalidate>
                       ... 

Answer

Alexander Leonov picture Alexander Leonov · Oct 16, 2017

You have to instantiate formgroup immediately on component creation, i.e. in the constructor, otherwise angular just cannot find what to bind template properties to.

UPDATE

Rephrasing: you have to instantiate form group before template gets rendered by angular. It's stricter than angular 1.x and throws an error if it cannot evaluate expression in template binding at the time of html form rendering.

Since you're using *ngIf="shop" in the template I'd say it means that shop turns not null before then() gets executed - maybe initially, maybe by some other function - it's not in the code you provided, so I can't point it out.

What you're trying to do is to initialize form with some data fetched by some service. That's totally fine - but it's still no reason to postpone creation of controls. Just create them in the constructor and set values later in the ngOnInit using FormGroup's setValue(), patchValue() or reset() - depending on what exactly you need. Below is just the idea, you'll need to adjust it to your form structure.

app.component.ts

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

    formGroup: FormGroup;

    constructor(fb: FormBuilder) {
        this.formGroup = fb.group({
            title: fb.control('initial value', Validators.required)
        });
    }

    ngOnInit(): void {
        this.formGroup.reset({title: 'new value'});
    }

}

app.component.html

<form [formGroup]="formGroup">
    <input type="text" formControlName="title">
</form>