אפשר לבנות טופס רגיל ואפשר לבנות טופס שהוא דינמי חלקי, זאת אומרת שעל פי בחירה שהמשתמש עושה יכולים להתוסף שדות נוספים שלא היו לפני כן.
אבל, בפוסט זה נדבר על בניה מלאה של טופס רק על ידי מידע שמגיע מהשרת.
זאת אומרת, אפשר לשנות את הרכב הטופס על ידי שינוי בבסיס הנתונים ואין צורך לערוך את הדף עצמו בצד האנגולר.
במילים אחרות:
angular reactive form dynamic fields data driven
דבר ראשון נוסיף את ReactiveFormsModule ל-app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormCompComponent } from './component/form-comp/form-comp.component';
import { InputTextCompComponent } from './component/input-text-comp/input-text-comp.component';
import { InputCheckboxCompComponent } from './component/input-checkbox-comp/input-checkbox-comp.component';
import { InputSelectCompComponent } from './component/input-select-comp/input-select-comp.component';
import { InputSwitcherCompComponent } from './component/input-switcher-comp/input-switcher-comp.component';
@NgModule({
declarations: [
AppComponent,
FormCompComponent,
InputTextCompComponent,
InputCheckboxCompComponent,
InputSelectCompComponent,
InputSwitcherCompComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
עכשיו לחלק המעניין, אני אראה כאן את הבסיס ליצירה של טופס דינמי וכמובן שאפשר לסבך את זה עוד הרבה 😊
המבנה מבחוץ ופנימה:
- form-comp – הדף הראשי שמחזיק בתגית form ובהגדרות של ההטופס
- input-switcher-comp – מחזיק 3 סוגי קומפוננטות ומעלה את הנכונה על פי המידע שמגיע לו מהדף הראשי
- 3 קומפוננטות – שדה טקסט, תיבת סימון ותיבת בחירה
אנחנו נקבל את הנתונים מקובץ מקומי ולא מקריאה לשרת והוא יחובר ישירות למשתנה ולא דרך שירות כדי לא להעמיס במידע את הפוסט הזה.
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { IFormComp } from './form-comp.interface';
import { formItemsData } from './form-comp.mock';
@Component({
selector: 'app-form-comp',
templateUrl: './form-comp.component.html',
styleUrls: ['./form-comp.component.scss']
})
export class FormCompComponent implements OnInit {
@Input() formData: IFormComp[] = formItemsData;
public gForm: FormGroup;
constructor() { }
ngOnInit() {
this.gForm = new FormGroup({})
this.gForm.addControl('static-input', new FormControl());
this.formData.map((item) => {
const fc = new FormControl();
this.gForm.addControl(item.name, fc);
});
}
sendData() {
console.log(this.gForm.controls);
}
}
export interface IFormComp {
name: string,
label: string,
type: string,
subData?: ISubData[]
}
interface ISubData {
text: string,
value: string
}import { IFormComp } from "./form-comp.interface";
export const formItemsData: IFormComp[] = [
{
label: 'first name',
name: 'firstName',
type: 'text',
},
{
label: 'last name',
name: 'lastName',
type: 'text',
},
{
label: 'are you happy?',
name: 'happy',
type: 'checkbox',
},
{
label: 'do you have a job?',
name: 'job',
type: 'checkbox',
},
{
label: 'select a country',
name: 'country',
type: 'select',
subData: [
{ text: 'israel', value: '1' },
{ text: 'usa', value: '2' },
{ text: 'canada', value: '3' }
]
},
{
label: 'select a city',
name: 'city',
type: 'select',
subData: [
{ text: 'tlv', value: '1' },
{ text: 'haifa', value: '2' },
{ text: 'toronto', value: '3' },
]
},
]<div>
<form action="" [formGroup]="gForm">
<ul>
<li>
<label><span>Title</span><input type="text" formControlName="static-input"></label>
</li>
<li *ngFor="let item of formData">
<app-input-switcher-comp [itemData]="item" [form]="gForm"></app-input-switcher-comp>
</li>
</ul>
<button (click)="sendData()">Send</button>
</form>
</div>עד כאן עשינו את ה-interface , mock ואת הדף הראשי שמכיל את הטופס, שהוא מורכב משדה אחד סטטי וכל השאר דינמי בלולאה שמפעילה את הממתג רכיבים שלנו.
הממתג
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { IFormComp } from '../form-comp/form-comp.interface';
@Component({
selector: 'app-input-switcher-comp',
templateUrl: './input-switcher-comp.component.html',
styleUrls: ['./input-switcher-comp.component.scss']
})
export class InputSwitcherCompComponent implements OnInit {
@Input() itemData: IFormComp = null;
@Input() form: FormGroup = null;
constructor() { }
ngOnInit() {
}
}
<div *ngIf="itemData.type==='text'">
<app-input-text-comp [itemData]="itemData" [form]="form"></app-input-text-comp>
</div>
<div *ngIf="itemData.type==='checkbox'">
<app-input-checkbox-comp [itemData]="itemData" [form]="form"></app-input-checkbox-comp>
</div>
<div *ngIf="itemData.type==='select'">
<app-input-select-comp [itemData]="itemData" [form]="form"></app-input-select-comp>
</div>הממתג מוכן, כמובן שיש דרכים יותר אלגנטיות לעשות את מה שכתוב למעלה אבל זו הדרך הכי פשוטה להבנה.
רכיבי השדות
טקסט
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { IFormComp } from '../form-comp/form-comp.interface';
@Component({
selector: 'app-input-text-comp',
templateUrl: './input-text-comp.component.html',
styleUrls: ['./input-text-comp.component.scss']
})
export class InputTextCompComponent implements OnInit {
@Input() form: FormGroup;
@Input() itemData: IFormComp = null;
constructor() { }
ngOnInit() {
}
}
<label [formGroup]="form">
<span>{{itemData.label}}</span>
<input type="text" [formControlName]="itemData.name">
</label>תיבת סימון
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { IFormComp } from '../form-comp/form-comp.interface';
@Component({
selector: 'app-input-checkbox-comp',
templateUrl: './input-checkbox-comp.component.html',
styleUrls: ['./input-checkbox-comp.component.scss']
})
export class InputCheckboxCompComponent implements OnInit {
@Input() form: FormGroup;
@Input() itemData: IFormComp = null;
constructor() { }
ngOnInit() {
}
}
<label [formGroup]="form">
<span>{{itemData.label}}</span>
<input type="checkbox" [formControlName]="itemData.name">
</label>שדה בחירה
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { IFormComp } from '../form-comp/form-comp.interface';
@Component({
selector: 'app-input-select-comp',
templateUrl: './input-select-comp.component.html',
styleUrls: ['./input-select-comp.component.scss']
})
export class InputSelectCompComponent implements OnInit {
@Input() form: FormGroup;
@Input() itemData: IFormComp = null;
constructor() { }
ngOnInit() {
}
}
<label [formGroup]="form">
<span>{{itemData.label}}</span>
<select [formControlName]="itemData.name">
<option *ngFor="let subData of itemData.subData" [value]="subData.value">{{subData.text}}</option>
</select>
</label>סיימנו!!
אם תריצו את הפרויקט ותדפיסו את gForm תוכלו לראות את כל השדות והערכים שלהם בטופס.
בהצלחה ותהנו לשכלל את הטופס





