6

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>
                       ... 
Burak
  • 5,406
  • 17
  • 64
  • 105

1 Answers1

15

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>
Alexander Leonov
  • 4,100
  • 1
  • 15
  • 25
  • `You have to instantiate formgroup immediately on component creation` Why? I think his code should work https://plnkr.co/edit/UwBTLPDAblb5ja33ZHwr?p=preview `angular just cannot find what to bind template properties ` Angular will bind properties as soon as ngIf becames true – yurzui Oct 16 '17 at 15:45
  • @yurzui, because I missed that ngIf. :( You're right, I'll update my answer to reflect that. Still, at any point in time form group must exist before template gets rendered by angular - in this case as soon as "shop" turns to be "truthy". So yes, there should be something else wrong with OP's code - probably because "shop" is not null initially or gets filled up by something else before that async call finishes and creates form group... and angular does try to render this form without form group. – Alexander Leonov Oct 16 '17 at 17:43
  • You can use `patchValue` or any other function in place of `reset` mentioned here - https://angular.io/api/forms/FormGroup#methods – Vibhor Dube Jun 04 '20 at 08:51