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