Skip to content

Instantly share code, notes, and snippets.

@mustafamagdy
Created February 25, 2018 17:27
Show Gist options
  • Select an option

  • Save mustafamagdy/6a5098b5d8aaa4bcf267b0d7d39d514c to your computer and use it in GitHub Desktop.

Select an option

Save mustafamagdy/6a5098b5d8aaa4bcf267b0d7d39d514c to your computer and use it in GitHub Desktop.
Service to generate model driven forms for ReactiveForms in angular from JSON structure
import { Injectable } from '@angular/core';
import { Validators, ValidatorFn, FormGroup, FormControl, AbstractControl } from '@angular/forms';
@Injectable()
export class ReactiveFormGeneratorService {
constructor() { }
/**
*
* @param model Json model in the following format
*
{
"name": "User",
"properties": {
"firstName": {
"type": "string",
"validators": ["required", "maxLength(100)"]
},
"lastName": {
"type": "string",
"validators": ["required", "maxLength(100)"]
},
"email": {
"type": "string",
"validators": ["email", "minLength(10)", "maxLength(100)"]
},
"address": {
"street": {
"type": "string",
"validators": []
},
"zip": {
"type": "number",
"validators": []
}
}
}
}
* Useage:
* this.form = new FormGroup(parseModel(....));
*/
parseModel(model: any): { [key: string]: AbstractControl } {
let properties = Object.entries(model.properties);
let fields: { [key: string]: AbstractControl } = {};
type property = { type: string, validators: [string] };
fields = this.parseTree(properties, fields);
return fields;
}
parseTree(properties: [string, {}][], fields: { [key: string]: AbstractControl }): { [key: string]: AbstractControl } {
properties.reduce((result, p, index) => {
if (p && p.length) {
let field = (p[1] as { type: string, validators: [string] });
if (field && typeof field.type === 'string' && field.validators instanceof Array) {
result[p[0]] = new FormControl('', this.parseValidators(field.type, field.validators));
}
else {
let gValidator = '';
let ar = Object.keys(p[1]).map(x => {
if (x == 'validator')
gValidator = p[1][x];
else
return [x, p[1][x]];
});
let gp = this.parseTree(ar as [string, {}][], {});
result[p[0]] = new FormGroup(gp);//, gValidator);
}
return fields;
}
}, fields);
return fields;
}
parseValidators(type: string, validators: [string]): ValidatorFn | ValidatorFn[] | null {
//if no validator sipploed, return null
if (!validators || !validators.length) return null;
//check if validator is supported for type
//this.validateValidValidators(type, validators);
let result = validators.map(v => {
let validatorAndParams = this.trimValidator(v);
if (validatorAndParams.args && validatorAndParams.args.length) {
return Validators[validatorAndParams.validtaor](validatorAndParams.args);
}
else {
return Validators[validatorAndParams.validtaor];
}
});
return result;
}
private validateValidValidators(type: string, validators: [string]) {
//type's supported validations
let validValidations = [
"string", ["required", "maxLength", "minLength", "email", "pattern"],
"number", ["required", "min", "max"],
"boolean", ["requiredTrue"],
]
let validValidationsForType = validValidations[type];
let inValidValidator = '';
if (validators.some(v => {
let notFound = validValidationsForType.indexOf(this.trimValidator(v).validtaor) == -1;
if (notFound)
inValidValidator = v;
return notFound;
})) {
throw `Validator ${inValidValidator} not supported for type ${type}`;
}
}
//shove off the validator and extract its parameters if exist
private trimValidator(validator: string): { validtaor: string, args: any[] } {
let splitter = new RegExp(/[(,)]/)
let parts = validator.split(splitter).filter(a => a.trim() != '');
let validatorFun = parts[0];
let args = parts.slice(1);
return { validtaor: validatorFun, args: args };
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment