Async Form group in presentation (formGroup expects a FormGroup instance)

416 Views Asked by At

I have a dumb and smart component, passing in a form that is async. So I have a value I pass: 'ready = false' that becomes true with the async call is complete, so that the form renders after the formGroup is populated. This worked perfectly before I split my component logic from one component to two (dumb and smart).

I'm not sure what is happening anymore, but i'm getting the error again: formGroup expects a FormGroup instance. Please pass one in.

Here is my 'dumb' .ts

import { Country } from './../modules-models/geo-name.models';
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
    selector: 'app-geo-drop',
    templateUrl: './geo-drop.component.html',
    styleUrls: ['./geo-drop.component.css']
})
export class GeoDropComponent {
    @Input() places: Array<Country>;
    @Input() ready: string;
    @Input() failed: string;
    @Input() countriesForm: FormGroup;
    @Output() toggleAllowed = new EventEmitter<Country>();
}

dumb .html

<div class="geo-list">
    <div class="content-box container">
        <form *ngIf="ready" [formGroup]="countriesForm">
            <div class="place col" *ngFor="let place of places" #holder>
                <input
                    type="checkbox"
                    formControlName="{{ place.name }}"
                    value="{{ place.allow }}"
                    (change)="toggleAllowed.emit(place)"
                    appSelect="place.allow"
                >
                {{ place.name }} | {{ place.code }} | {{ place.continent }}
            </div>
        </form>
        <div *ngIf="failed === 'true'" class="error">
            The Request To Server Failed
        </div>
    </div>
</div>

smart .ts

import { Countries, Country } from './../modules-models/geo-name.models';
import { WhiteListService } from './../geo-drop/white-list.service';
import { FormGroup, FormControl } from '@angular/forms';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-geo-drop-view',
  templateUrl: './geo-drop-view.component.html',
  styleUrls: ['./geo-drop-view.component.css']
})
export class GeoDropViewComponent implements OnInit {
    places;
    ready = false;
    failed = false;
    countriesForm: FormGroup;

    constructor(private whiteListService: WhiteListService) {}

    ngOnInit() {
        // get places list with status'
        this.whiteListService.getList()
            .subscribe(
                (response: Countries) => {
                    this.places = response.countries;
                    this.createList(this.places);
                },
                (error) => {
                    console.log(error);
                    this.failed = true;
                }
            );
    }

    createList(places) {
        // assign this.places for dom binding access
        this.places = places;

        // create reactive && dynamic form checklist
        this.countriesForm = new FormGroup({});
        for (let i = 0; i < this.places.length; i++) {
            this.countriesForm.addControl(
                this.places[i].name, new FormControl(this.places[i].allow == 1 ? 1 : 0)
            );
        }
        console.log(this.countriesForm);
        this.ready = true;
    }

    toggleAllowed(place) {
        // switch local model of place authorization
        place.allow == 1 ? place.allow = 0 : place.allow = 1;

        // send authorization of country to server
        this.whiteListService.sendAllow(place.code, place.allow)
            .subscribe(
                (response) => console.log(response),
                (error) => {
                    console.log(error);
                    // if auth is not sent OK, change local model back
                    // to its original status
                    place.allow == 1 ? place.allow = 0 : place.allow = 1;
                }
        );
    }
}

smart .html

<app-geo-drop
    [places]="places"
    [ready]="ready"
    [failed]="failed"
    [formGroup]="countriesForm"
    (toggleAllowed)="toggleAllowed($event)"
>
</app-geo-drop>
1

There are 1 best solutions below

1
On BEST ANSWER

formGroup is a directive. Using this name as component input in [formGroup]="countriesForm" results in compiling it on the element.

The input in child component shouldn't be named formGroup.