I'm trying to master dynamic forms following the official documentation but with my own case.
I have api that I'm trying to fetch data from https://demo7782009.mockable.io/form and print it out in corresponding form fields. Each field has own validation requirements.
But getting an error
Error: formGroup expects a FormGroup instance. Please pass one in.
Example: <div [formGroup]="myGroup"> <input formControlName="firstName"> </div> In your class: this.myGroup = new FormGroup({ firstName: new FormControl() });
I've tried to make a plunker with my app, but it doesn't bootstrap
So there is my code (sorry for that)
App.component.ts
import { FetchFormService } from './fetch-form.service';
import { QuestionControlService } from './question-control.service';
import { Component, OnInit } from '@angular/core';
import { QuestionBase } from './Models/question-base';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
questions: QuestionBase<any>[];
form: FormGroup;
constructor(
private qcs: QuestionControlService,
private ffs: FetchFormService
) {
}
ngOnInit() {
this.ffs.getData().subscribe(data => {
this.questions = data.data;
let formQuestions: [QuestionBase<any>];
this.questions.forEach(e => {
formQuestions.push(new QuestionBase(e));
});
this.form = this.qcs.toFormGroup(formQuestions);
});
}
}
App.component.html
<form [formGroup]="form">
<div *ngFor="let question of questions; let i = index">
<app-question-item [question]="question" [index]='i' [form]="form"></app-question-item>
</div>
<button [disabled]="!form.valid">Submit</button>
</form>
QuestionBase model
export class QuestionBase<T> {
value: T;
values?: string[];
label: string;
type: string;
constructor(options: {
value: T,
values?: string[],
label: string,
type: string
}) {
this.value = options.value;
this.label = options.label;
this.type = options.type;
this.values = options.values;
}
}
Question-item.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { QuestionBase } from '../Models/question-base';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-question-item',
templateUrl: './question-item.component.html',
styleUrls: ['./question-item.component.css']
})
export class QuestionItemComponent implements OnInit {
@Input() question: QuestionBase<any>;
@Input() form: FormGroup;
@Input() index;
constructor() { }
ngOnInit() {
}
}
Question-item.component.html
<div [formGroup]="form">
<label>
{{ question.label }}
<ng-container [ngSwitch]="question.type">
<input [formControlName]="'field' + index" *ngSwitchCase="'string' || 'integer' || 'double'" type="text">
<textarea [formControlName]="'field' + index" *ngSwitchCase="'text'" cols="30" rows="10"></textarea>
<ul *ngSwitchCase="'list'">
<li *ngFor="let item of question.values; let subindex = index">
<input [formControlName]="'field' + index + subindex" type="text">
</li>
</ul>
</ng-container>
</label>
</div>
Fetch-form.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
interface Form {
data: {
type: string,
label: string,
value: string,
values?: string[]
}[];
}
@Injectable({
providedIn: 'root'
})
export class FetchFormService {
constructor(
private http: HttpClient
) { }
getData() {
return this.http.get<Form>('https://demo7782009.mockable.io/form');
}
}
Question-control.service.ts
import { QuestionBase } from './Models/question-base';
import { Injectable } from '@angular/core';
import { FormControl, Validators, FormGroup } from '@angular/forms';
@Injectable({
providedIn: 'root'
})
export class QuestionControlService {
constructor() { }
toFormGroup(questsions: QuestionBase<any>[]) {
let group: any = {};
questsions.forEach((e, i) => {
if (e.type === 'string' || e.type === 'text' || e.type === 'list') {
group[i] = new FormControl(e.value, Validators.required);
} else if (e.type === 'integer') {
group[i] = new FormControl(e.value, [Validators.required, Validators.pattern(/\d+/)]);
} else if (e.type === 'double') {
group[i] = new FormControl(e.value, [Validators.required, Validators.pattern(/\d+\.\d+/)]);
} else {}
});
return new FormGroup(group);
}
}
App.module.ts
import { QuestionItemComponent } from './question-item/question-item.component';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
QuestionItemComponent
],
imports: [
BrowserModule,
ReactiveFormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Continuation of this questions is here
Angular 6 Dynamic Forms issue [part 2]