Angular 4+, Empower Dynamic Form, Part 2: Make It Better

import { MyModelParam } from './myModelParam';export class Address {
street: string;
location: string;
constructor(param : MyModelParam) {
this.street = param.street || 'guess...';
this.location = param.location || 'Heaven';
}
}
import { MyModelParam } from './myModelParam';
import { Type } from './type';
export class Sex {
type: string;
sexy: string;
constructor(param : MyModelParam) {
this.type = param.type || Type.MyLady.toString();
this.sexy = param.sexy || 'Yes!';
}
}
import { Type } from './type';export interface MyModelParam {
name?: string; //<-optional now
type?: string;
sexy?: string;
street?: string;
location?: string

}
import { Address } from './address';
import { MyModelParam } from './myModelParam';
import { Sex } from './sex';
import { Type } from './type';
export class MyModel {
name: string;
sex: Sex;
address: Address;
constructor(param : MyModelParam) {
this.name = param.name || '';
this.sex = new Sex({type: param.type, sexy: param.sexy});
this.address = new Address({street: param.street, location: param.location});

}
}
ng g component group-element
import { ElementParam } from '../form-element/elementParam';
import { FormElement } from '../form-element/formElement';
import { AbstractControl, FormGroup } from '@angular/forms';
export class Group extends FormElement {
elementType = 'group';
elements: FormElement[];
constructor(element: ElementParam) {
super(element);
this.elements = element.elements;
}
toFormControl(): AbstractControl {
return new FormGroup(this.toFormGroup(this.elements));
}
private toFormGroup(elements) {
let group: any = {};
elements.forEach((element: FormElement) => {
if (element.elementType === 'group') {
let subGrp: any = this.toFormGroup((element as Group).elements);
group[element.name] = new FormGroup(subGrp);
} else {
group[element.name] = element.toFormControl();
}
});

return group;
}
}
import { Type } from '../model/type';
import { FormElement } from './formElement';
export interface ElementParam {
name: string;
displayName?: string;
elementType?: string;
value?: any;
required?: boolean;
order?: number;
//'elements' field represents a group of elements
elements?: FormElement[];

layout?: any;
options?: string[];
}
import { DropDown } from '../form-element/dropDown';
import { FormElement } from '../form-element/formElement';
import { TextField } from '../form-element/textField';
import { Group } from '../group-element/group';
import { MyModel } from '../model/myModel';
import { Type } from '../model/type';
import { FormGroup, AbstractControl, FormControl } from '@angular/forms';
export class FormFactory {
elements: FormElement[];
private toFormGroup(elements: FormElement[]): AbstractControl {
let group: any = {};
elements.forEach((element: FormElement) => {
group[element.name] = element.toFormControl();
});

return new FormGroup(group);
}
public createForm(model: MyModel): FormGroup {
console.debug('creating form...');

this.elements = [
new Group({
name: 'form',
layout: {width: '80%', fontSize: '12', fx: 'column' },
elements: [
new TextField({
name: 'name',
displayName: 'Name',
value: model.name,
layout: {float: 'left', width: '80%', fontSize: '16' }
}),
new Group({
name: 'sex',
displayName: 'Sex',
layout: {float: 'right', width: '80%', labelFontSize: '14', fx: 'column', fxAlign: 'start', lableFontWeight: 'bold' },
elements: [
new DropDown({
name: 'type',
displayName: 'Type',
value: model.sex.type,
options: [Type.MyLady.toString(), Type.MyMan.toString()],
layout: {float: 'left', width: '80%',fontSize: '16'}
}),
new DropDown({
name: 'sexy',
displayName: 'Sexy',
value: model.sex.sexy,
options: ['Yes!', 'Oh, Yeah!'],
layout: {float: 'left', width: '80%',fontSize: '16'}
}),
],
}),
new Group({
name: 'address',
displayName: 'Address',
layout: {float: 'right', width: '80%', labelFontSize: '14', fx: 'column', fxAlign: 'start', lableFontWeight: 'bold' },
elements: [
new TextField({
name: 'street',
displayName: 'Street',
value: model.address.street,
layout: {float: 'left', width: '80%', fontSize: '16' }
}),
new DropDown({
name: 'location',
displayName: 'Location',
value: model.address.location,
options: ['Heaven', 'Under the tree', 'On the green grass', 'Among the beautiful flowers', 'Down to earth'],
layout: {float: 'left', width: '80%', fontSize: '16' }
}),
],
})
]
})
];
let ret: FormGroup = this.toFormGroup(this.elements) as FormGroup;
return ret;
}
}
{
"form":{
"name":"Eve",
"sex":{
"type":"My Lady",
"sexy":"Yes!"
},
"address":{
"street":"guess...",
"location":"Heaven"
}
}
}
{
"form":{
"name":"Adam",
"sex":{
"type":"My Man",
"sexy":"Oh, Yeah!"
},
"address":{
"street":"801 Who-knows",
"location":"Under the tree"
}
}
}
<div [formGroup]="group" style="border:0px;">  <mat-label fxLayoutAlign="{{element.layout.fxAlign}}" 
[ngStyle]="{'float': element.layout.float,
'font-size.px': element.layout.labelFontSize,
'font-weight': element.layout.lableFontWeight}">
{{element.displayName}}
</mat-label>
<!-- That is right. This is the plain dumb br here! I believe you could do better...-->
<br>
<mat-card class="card" fxLayout="{{element.layout.fx}}" fxLayoutGap="{{element.layout.fxGap}}"> <div *ngFor="let elemt of element.elements"> <div [ngSwitch]="elemt.elementType" class='panel-body' style="border:0px;"> <!-- This app-form-element allows the form elements in this group displayed -->
<app-form-element *ngSwitchDefault [group]="group" [element]="elemt" ></app-form-element>
<!-- This app-group-element allows the nested groups displayed -->
<app-group-element *ngSwitchCase="'group'" [group]="group.get(elemt.name)" [element]="elemt"></app-group-element>
</div>
</div>
</mat-card>
</div><br>
import { FormElement } from '../form-element/formElement';
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-group-element',
templateUrl: './group-element.component.html',
styleUrls: ['./group-element.component.css']
})
export class GroupElementComponent implements OnInit {

@Input() element: FormElement;
@Input() group: FormGroup;
constructor() { } ngOnInit() { }
}
<form (ngSubmit)="submit()" [formGroup]="form" >  <div style="width: 40%; margin: auto;" >

<div *ngFor="let elemt of elements"
[ngStyle]="{'float': elemt.layout.float,
'width': elemt.layout.width}">
<div [ngSwitch]="elemt.elementType" class='panel-body' style="border:0px;"> <app-form-element *ngSwitchDefault [group]="form" [element]="elemt" ></app-form-element> <app-group-element *ngSwitchCase="'group'" [group]="form.get(elemt.name)" [element]="elemt"> </app-group-element>

</div>

</div>
<button mat-button type="submit">Submit</button> </div>
</form>

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store