UNPKG

rx-form-mapper

Version:

Proper decorator-based transformation / serialization / deserialization of plain javascript classes to angular reactive forms

404 lines (380 loc) 13.7 kB
import 'reflect-metadata'; import { negate, isNil, castArray, isFunction } from 'lodash'; import { InjectFlags, Injectable, Injector, NgModule, Optional, SkipSelf } from '@angular/core'; import { FormArray as FormArray$1, FormControl as FormControl$1, FormGroup as FormGroup$1 } from '@angular/forms'; const isNotNil = negate(isNil); function coerceArray(value) { return castArray(value).filter(isNotNil); } function isType(value) { return isFunction(value); } class ValidableMetadata { constructor() { this._validators = []; this._asyncValidators = []; } setValidators(opts) { this._validators = coerceArray(opts === null || opts === void 0 ? void 0 : opts.validators); this._asyncValidators = coerceArray(opts === null || opts === void 0 ? void 0 : opts.asyncValidators); this._updateOn = opts === null || opts === void 0 ? void 0 : opts.updateOn; } get validators() { return this._validators; } get asyncValidators() { return this._asyncValidators; } get updateOn() { return this._updateOn; } } class CustomControlMetadata extends ValidableMetadata { constructor(mapper) { super(); this.mapper = mapper; } accept(visitor) { return visitor.visitCustomControlMetadata(this); } } class FormArrayMetadata extends ValidableMetadata { constructor(itemForm) { super(); this.itemForm = itemForm; } accept(visitor) { return visitor.visitFormArrayMetadata(this); } } class FormControlMetadata extends ValidableMetadata { accept(visitor) { return visitor.visitFormControlMetadata(this); } } class FormGroupMetadata { constructor(form) { this.form = form; } accept(visitor) { return visitor.visitFormGroupMetadata(this); } } class FormMetadata extends ValidableMetadata { constructor(type) { super(); this.type = type; this._controls = {}; } accept(visitor) { return visitor.visitFormMetadata(this); } get controls() { return this._controls; } setFormControl(name, opts) { const control = new FormControlMetadata(); control.setValidators(opts); this.controls[name] = control; } setFormGroup(name, form) { const control = new FormGroupMetadata(form); this.controls[name] = control; } setCustomControl(name, mapper, opts) { const control = new CustomControlMetadata(mapper); control.setValidators(opts); this.controls[name] = control; } setFormArray(name, itemform, opts) { const control = new FormArrayMetadata(itemform); control.setValidators(opts); this.controls[name] = control; } } class ModelBinder { constructor() { this.metadataKey = 'rx-form-mapper-metadata'; } getMetadata(target) { if (!Reflect.hasMetadata(this.metadataKey, target)) { Reflect.defineMetadata(this.metadataKey, new FormMetadata(target), target); } return Reflect.getMetadata(this.metadataKey, target); } bindForm(target, opts) { const formMetadata = this.getMetadata(target); formMetadata.setValidators(opts); } bindCustomControl(target, propertyName, opts) { this.getMetadata(target.constructor).setCustomControl(propertyName, opts.mapper, opts); } bindFormControl(target, propertyName, opts) { this.getMetadata(target.constructor).setFormControl(propertyName, opts); } bindFormGroup(target, propertyName, type) { const propertyType = type !== null && type !== void 0 ? type : Reflect.getMetadata(MetadataDesignTypes.TYPE, target, propertyName); const propertyFormMetadata = this.getMetadata(propertyType); this.getMetadata(target.constructor).setFormGroup(propertyName, propertyFormMetadata); } bindFormArray(target, propertyName, opts) { const itemFormMetadata = this.getMetadata(opts.type); this.getMetadata(target.constructor).setFormArray(propertyName, itemFormMetadata, opts); } } ModelBinder.instance = new ModelBinder(); var MetadataDesignTypes; (function (MetadataDesignTypes) { MetadataDesignTypes["TYPE"] = "design:type"; MetadataDesignTypes["RETURN_TYPE"] = "design:returntype"; MetadataDesignTypes["PARAM_TYPE"] = "design:paramtypes"; })(MetadataDesignTypes || (MetadataDesignTypes = {})); function CustomControl(optsOrMapper) { return (target, propertyName) => { if (isNil(optsOrMapper)) { throw new Error(`unexpected CustomControl configuration: ${optsOrMapper}`); } if (typeof (optsOrMapper) !== 'object') { optsOrMapper = { mapper: optsOrMapper }; } ModelBinder.instance.bindCustomControl(target, propertyName, optsOrMapper); }; } function FormArray(optsOrType) { return (target, propertyName) => { if (isNil(optsOrType)) { throw new Error(`unexpected FormArray configuration: ${optsOrType}`); } if (isType(optsOrType)) { optsOrType = { type: optsOrType }; } ModelBinder.instance.bindFormArray(target, propertyName, optsOrType); }; } function FormControl(opts) { return (target, propertyName) => { ModelBinder.instance.bindFormControl(target, propertyName, opts); }; } function FormGroup(type) { return (target, propertyName) => { ModelBinder.instance.bindFormGroup(target, propertyName, type); }; } function Form(opts) { return (target) => { ModelBinder.instance.bindForm(target, opts); }; } class CustomMapperResolver { constructor(injector) { this.injector = injector; } resolve(type) { var _a; return (_a = this.injector.get(type, null, InjectFlags.Optional)) !== null && _a !== void 0 ? _a : new type(); } } CustomMapperResolver.decorators = [ { type: Injectable } ]; CustomMapperResolver.ctorParameters = () => [ { type: Injector } ]; class FormReader { constructor(control, customMapperResolver) { this.control = control; this.customMapperResolver = customMapperResolver; } visitCustomControlMetadata(customControlMetadata) { return this.customMapperResolver.resolve(customControlMetadata.mapper).readForm(this.control); } visitFormArrayMetadata(formArrayMetadata) { if (!this.control) { return void (0); } this.checkControlType(this.control, FormArray$1); return this.control.controls.map(control => { const subFormReader = this.copyPrototype(control); return formArrayMetadata.itemForm.accept(subFormReader); }); } visitFormControlMetadata(formControlMetadata) { if (!this.control) { return void (0); } this.checkControlType(this.control, FormControl$1); return this.control.value; } visitFormGroupMetadata(formGroupMetadata) { return this.visitFormMetadata(formGroupMetadata.form); } visitFormMetadata(formMetadata) { if (!this.control) { return void (0); } this.checkControlType(this.control, FormGroup$1); const value = new formMetadata.type(); const formGroup = this.control; for (const [key, controlMetadata] of Object.entries(formMetadata.controls)) { const formField = formGroup.controls[key]; const formFieldReader = this.copyPrototype(formField); value[key] = controlMetadata.accept(formFieldReader); } return value; } copyPrototype(control) { return new FormReader(control, this.customMapperResolver); } checkControlType(control, type) { if (control instanceof type) { return; } throw new Error(`control is not ${type.name} instance`); } } class FormWriter { constructor(value, customMapperResolver, validatorResolver) { this.value = value; this.customMapperResolver = customMapperResolver; this.validatorResolver = validatorResolver; } visitCustomControlMetadata(customControlMetadata) { const mapper = this.customMapperResolver.resolve(customControlMetadata.mapper); return mapper.writeForm(this.value, this.buildAbstractControlOptions(customControlMetadata)); } visitFormArrayMetadata(formArrayMetadata) { var _a; const values = (_a = this.value) !== null && _a !== void 0 ? _a : []; const controls = []; for (const item of values) { const subWriter = this.copyPrototype(item); const control = formArrayMetadata.itemForm.accept(subWriter); controls.push(control); } return new FormArray$1(controls, this.buildAbstractControlOptions(formArrayMetadata)); } visitFormControlMetadata(formControlMetadata) { return new FormControl$1(this.value, this.buildAbstractControlOptions(formControlMetadata)); } visitFormGroupMetadata(formGroupMetadata) { return this.visitFormMetadata(formGroupMetadata.form); } visitFormMetadata(formMetadata) { var _a; const controls = {}; for (const [key, controlMetadata] of Object.entries(formMetadata.controls)) { const fieldValue = (_a = this.value) === null || _a === void 0 ? void 0 : _a[key]; const subWriter = this.copyPrototype(fieldValue); controls[key] = controlMetadata.accept(subWriter); } return new FormGroup$1(controls, this.buildAbstractControlOptions(formMetadata)); } buildAbstractControlOptions(metadata) { return { validators: metadata.validators.map(v => this.validatorResolver.resolve(v)), asyncValidators: metadata.asyncValidators.map(v => this.validatorResolver.resolve(v)), updateOn: metadata.updateOn }; } copyPrototype(value) { return new FormWriter(value, this.customMapperResolver, this.validatorResolver); } } class ValidatorResolver { constructor(injector) { this.injector = injector; } resolve(validator) { if (this.isValidatorFn(validator)) { return validator; } let validatorInstance = null; if (this.isValidatorInstance(validator)) { validatorInstance = validator; } else { validatorInstance = this.injector.get(validator, null, InjectFlags.Optional); validatorInstance !== null && validatorInstance !== void 0 ? validatorInstance : (validatorInstance = new validator()); } return (c) => validatorInstance.validate(c); } isValidatorFn(value) { return isFunction(value) && !value.prototype.validate; } isValidatorInstance(value) { return 'validate' in value; } } ValidatorResolver.decorators = [ { type: Injectable } ]; ValidatorResolver.ctorParameters = () => [ { type: Injector } ]; class RxFormMapper { constructor(customMapperResolver, validatorResolver) { this.customMapperResolver = customMapperResolver; this.validatorResolver = validatorResolver; } fromType(type) { if (isNil(type)) { throw new Error('type cannot be inferred implicitly'); } const formWriter = new FormWriter(void (0), this.customMapperResolver, this.validatorResolver); return ModelBinder.instance.getMetadata(type).accept(formWriter); } writeForm(value, type) { if (isNil(value) && isNil(type)) { throw new Error('type cannot be inferred implicitly'); } const valueType = type !== null && type !== void 0 ? type : Object.getPrototypeOf(value).constructor; const formWriter = new FormWriter(value, this.customMapperResolver, this.validatorResolver); return ModelBinder.instance.getMetadata(valueType).accept(formWriter); } readForm(form, type) { if (isNil(type)) { throw new Error('type cannot be inferred implicitly'); } const formReader = new FormReader(form, this.customMapperResolver); return ModelBinder.instance.getMetadata(type).accept(formReader); } } RxFormMapper.decorators = [ { type: Injectable } ]; RxFormMapper.ctorParameters = () => [ { type: CustomMapperResolver }, { type: ValidatorResolver } ]; class RxFormMapperModule { constructor(parentModule) { if (parentModule) { throw new Error('RxFormMapperModule is already loaded. Import it in the AppModule only'); } } static forRoot() { return { ngModule: RxFormMapperModule, providers: [ RxFormMapper, CustomMapperResolver, ValidatorResolver ] }; } } RxFormMapperModule.decorators = [ { type: NgModule } ]; RxFormMapperModule.ctorParameters = () => [ { type: RxFormMapperModule, decorators: [{ type: Optional }, { type: SkipSelf }] } ]; /* * Public API Surface of rx-form-mapper */ /** * Generated bundle index. Do not edit. */ export { CustomControl, CustomControlMetadata, CustomMapperResolver, Form, FormArray, FormArrayMetadata, FormControl, FormControlMetadata, FormGroup, FormGroupMetadata, FormMetadata, FormReader, FormWriter, MetadataDesignTypes, ModelBinder, RxFormMapper, RxFormMapperModule, ValidableMetadata, ValidatorResolver, coerceArray, isType, RxFormMapper as ɵa, CustomMapperResolver as ɵb, ValidatorResolver as ɵc }; //# sourceMappingURL=rx-form-mapper.js.map