Reputation: 41
I am new to angular and recently working on creating a dynamic form with validations from response given by api. The api will provide the form field like Date type input type or multi select. Also the api response will provide the validations like field is required or not and regex for the input field if exist.
Using all the above data I need to create a form in angular which should be working like a normal form with validations like touched, untouched, error on reuqired field, error on validators etc.
Public URL of the page created using html and javascript. The api will be available from network tabs
https://devemr.growthemr.com/assets/static/form.html?bid=1413&fid=9242
Below is the code base I tried.
form.component.ts
ngOnInit(): void {
this.loadQuestions(); // it is the API call and I have attached the sample API response
this.form = this.fb.group({
items: this.fb.array([])
})
}
addItems() {
this.contactFormQuestions.forEach((x: any) => {
this.itemsFormArray.push(this.fb.group({
[x.id]: ['', x.required ?[ Validators.required] : null, x.regex? [Validators.pattern(x.regex)]: ''],
}));
});
console.log(this.form.get('items'));
}
get itemsFormArray() {
return this.form.get('items') as FormArray;
}
form.component.html
<form [formGroup]="form">
<div formArrayName="items">
<div *ngFor="let pqa of itemsFormArray.controls; let i = index">
<div [formGroupName]="i">
<div [ngSwitch]="pqa.value.questionType" class="switchCase">
<div *ngSwitchCase="'Input'" class="input-feild">
<input type="text" class="form-control" [formControlName]="pqa.value.id">
</div>
<div *ngSwitchCase="'Date'" class="input-feild">
<input type="date" class="form-control" [formControlName]="pqa.value.id">
</div>
</div>
</div>
</div>
</div>
</form>
API response
[
{
"id": 59233,
"questionId": 74719,
"questionName": "fname",
"questionType": "Input",
"hidden": null,
"required": true,
"validate": null,
"regex": "[A-Za-z]",
"validationMessage": null,
"answer": false,
"questionChoices": null,
"patientQuestionChoices": [],
"allowMultipleSelection": false,
"showDropDown": null,
"preSelectCheckbox": false
},
{
"id": 59234,
"questionId": 74720,
"questionName": "Date",
"questionType": "Date",
"hidden": null,
"required": true,
"validate": null,
"regex": null,
"validationMessage": null,
"answer": false,
"questionChoices": null,
"patientQuestionChoices": [],
"allowMultipleSelection": false,
"showDropDown": null,
"preSelectCheckbox": false
},
{
"id": 59235,
"questionId": 74721,
"questionName": "Multi Select",
"questionType": "Multiple_Selection_Text",
"hidden": null,
"required": true,
"validate": null,
"regex": null,
"validationMessage": null,
"answer": false,
"questionChoices": null,
"patientQuestionChoices": [
{
"deleted": false,
"tenantId": 1413,
"id": 3993,
"choiceName": "Option1",
"choiceId": 8546,
"selected": false
},
{
"deleted": false,
"tenantId": 1413,
"id": 3994,
"choiceName": "Option2",
"choiceId": 8547,
"selected": false
}
],
"allowMultipleSelection": true,
"showDropDown": true,
"preSelectCheckbox": false
}
]
Upvotes: 1
Views: 1863
Reputation: 2531
Here you have some code to create a ReactiveForm. Maybe that can help you to solve your problem.
Possible way
Once you have multiple components for inputs form (text,date,multi-select,...), that should be possible to loop on your API response and test questionType
to build a form dynamically (ReactiveForm in ts and inputs components in html).
Form component ts
// Reactive Forms
form: FormGroup;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
// Form structure and validators
this.form = this.formBuilder.group({
'user' : this.formBuilder.group({
'username' : ['', Validators.required],
'email' : ['', [Validators.required, Validators.email]]
}),
'identity' : this.formBuilder.group({
'firstname' : ['', Validators.required],
'lastname' : ['', Validators.required],
'address' : this.formBuilder.group({
'street' : ['', Validators.required],
'city' : ['', Validators.required],
})
})
});
}
onSubmit() {
// Get object with same structure as form but only with values
console.log(this.form.value);
alert('Form is ' + (this.form.invalid ? 'invalid' : 'valid'));
}
Form component html
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<form-text [formGroupParent]="form.get(['user'])"
[formGroupControlName]="'username'">
</form-text>
<form-text [formGroupParent]="form.get(['user'])"
[formGroupControlName]="'email'">
</form-text>
<hr>
<form-text [formGroupParent]="form.get(['identity'])"
[formGroupControlName]="'firstname'">
</form-text>
<form-text [formGroupParent]="form.get(['identity'])"
[formGroupControlName]="'lastname'">
</form-text>
<hr>
<form-text [formGroupParent]="form.get(['identity','address'])"
[formGroupControlName]="'street'">
</form-text>
<form-text [formGroupParent]="form.get(['identity','address'])"
[formGroupControlName]="'city'">
</form-text>
<button type="submit">Submit</button>
</form>
Custom input component ts (form-text)
// Needed to bind formControlName
@Input() formGroupParent: FormGroup;
@Input() formGroupControlName: string;
// FormControl store validators
control: FormControl;
ngOnInit() {
// Fetch Form control (validator) from FormGroup parent
this.control = <FormControl>this.formGroupParent.get(this.formGroupControlName);
}
Custom input component html (form-text)
<ng-container [formGroup]="formGroupParent">
<label>{{formGroupControlName}}</label>
<input type="text" formControlName="{{formGroupControlName}}">
</ng-container
External lib
For info (not always easy to integrate), a great library exists to design and manage survey : SurveyJS.
Upvotes: 1
Reputation: 57929
Take a look the docs
The idea is basically
1.-Create the formGroup
getForm(json:any){
const group=new FormGroup({})
json.forEach(x=>{
const validators=[];
if (x.required)
validators.push(Validator.required)
if (x.regex)
validators.push(Validator.patern(x.regex)
....
group.addControl(x.questionName,new FormControl("",validators)
}
return group
}
2.-Iterate over form.controls.|keyvalue and crate the elements using the data in json
<form [formGroup]="form">
<div *ngFor="let keyvalue of form.controls|keyvalue;let i=index">
<app-control [data]="json[i]" ></app-control>
</div>
</form>
Your app-control take a count the json data and use viewProviders. This make that you can use formControlName or formGroupName or ...
@Component({
selector: '..',
templateUrl: '...'
viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
})
@Input()data;
Your .html like
<input *ngIf="data.questionType=='Input'"
[formControlName]="data.questionName">
<ng-container *ngIf="data.questionType=='Multiple_Selection_Text'">
...
</ng-container>
...
Upvotes: 0