wfw-ngx-formly
Version:
ngx-formly is an Angular 2 module which has a Components to help customize and render JavaScript/JSON configured forms. The formly-form Component and the FormlyConfig service are very powerful and bring unmatched maintainability to your application's form
1,933 lines • 62.9 kB
JavaScript
import { Observable, Subject } from 'rxjs';
import { Injectable, Inject, InjectionToken, Component, Input, Optional, EventEmitter, Output, SkipSelf, ViewContainerRef, ViewChild, ComponentFactoryResolver, Directive, HostListener, ElementRef, Renderer2, NgModule, ANALYZE_FOR_ENTRY_COMPONENTS } from '@angular/core';
import { FormGroup, FormArray, FormControl, AbstractControl, Validators, NgForm, FormGroupDirective, ReactiveFormsModule } from '@angular/forms';
import { debounceTime, map, tap } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @param {?} formId
* @param {?} field
* @param {?} index
* @return {?}
*/
function getFieldId(formId, field, index) {
if (field.id)
return field.id;
let /** @type {?} */ type = field.type;
if (!type && field.template)
type = 'template';
return [formId, type, field.key, index].join('_');
}
/**
* @param {?} field
* @return {?}
*/
function getKeyPath(field) {
/* We store the keyPath in the field for performance reasons. This function will be called frequently. */
if (!(/** @type {?} */ (field))['_formlyKeyPath'] || (/** @type {?} */ (field))['_formlyKeyPath'].key !== field.key) {
let /** @type {?} */ keyPath = [];
if (field.key) {
/* Also allow for an array key, hence the type check */
let /** @type {?} */ pathElements = typeof field.key === 'string' ? field.key.split('.') : field.key;
for (let /** @type {?} */ pathElement of pathElements) {
if (typeof pathElement === 'string') {
/* replace paths of the form names[2] by names.2, cfr. angular formly */
pathElement = pathElement.replace(/\[(\w+)\]/g, '.$1');
keyPath = keyPath.concat(pathElement.split('.'));
}
else {
keyPath.push(pathElement);
}
}
for (let /** @type {?} */ i = 0; i < keyPath.length; i++) {
let /** @type {?} */ pathElement = keyPath[i];
if (typeof pathElement === 'string' && stringIsInteger(pathElement)) {
keyPath[i] = parseInt(pathElement);
}
}
}
(/** @type {?} */ (field))['_formlyKeyPath'] = {
key: field.key,
path: keyPath,
};
}
return (/** @type {?} */ (field))['_formlyKeyPath'].path.slice(0);
}
/**
* @param {?} str
* @return {?}
*/
function stringIsInteger(str) {
return !isNullOrUndefined(str) && /^\d+$/.test(str);
}
const FORMLY_VALIDATORS = ['required', 'pattern', 'minLength', 'maxLength', 'min', 'max'];
/**
* @param {?} model
* @param {?} field
* @param {?} constructEmptyObjects
* @return {?}
*/
function getFieldModel(model, field, constructEmptyObjects) {
let /** @type {?} */ keyPath = getKeyPath(field);
let /** @type {?} */ value = model;
for (let /** @type {?} */ i = 0; i < keyPath.length; i++) {
let /** @type {?} */ path = keyPath[i];
let /** @type {?} */ pathValue = value[path];
if (isNullOrUndefined(pathValue) && constructEmptyObjects) {
if (i < keyPath.length - 1) {
/* TODO? : It would be much nicer if we could construct object instances of the correct class, for instance by using factories. */
value[path] = typeof keyPath[i + 1] === 'number' ? [] : {};
}
else if (field.fieldGroup && !field.fieldArray) {
value[path] = {};
}
else if (field.fieldArray) {
value[path] = [];
}
}
value = value[path];
if (!value) {
break;
}
}
return value;
}
/**
* @param {?} fields
* @param {?} model
* @return {?}
*/
function assignModelToFields(fields, model) {
fields.forEach((field, index) => {
if (!isUndefined(field.defaultValue) && isUndefined(getValueForKey(model, field.key))) {
assignModelValue(model, field.key, field.defaultValue);
}
(/** @type {?} */ (field)).model = model;
if (field.key && (field.fieldGroup || field.fieldArray)) {
(/** @type {?} */ (field)).model = getFieldModel(model, field, true);
}
if (field.fieldGroup) {
assignModelToFields(field.fieldGroup, field.model);
}
});
}
/**
* @param {?} model
* @param {?} path
* @param {?} value
* @return {?}
*/
function assignModelValue(model, path, value) {
if (typeof path === 'string') {
path = getKeyPath({ key: path });
}
if (path.length > 1) {
const /** @type {?} */ e = path.shift();
if (!model[e] || !isObject(model[e])) {
model[e] = typeof path[0] === 'string' ? {} : [];
}
assignModelValue(model[e], path, value);
}
else {
model[path[0]] = value;
}
}
/**
* @param {?} model
* @param {?} path
* @return {?}
*/
function getValueForKey(model, path) {
if (typeof path === 'string') {
path = getKeyPath({ key: path });
}
if (path.length > 1) {
const /** @type {?} */ e = path.shift();
if (!model[e]) {
model[e] = typeof path[0] === 'string' ? {} : [];
}
return getValueForKey(model[e], path);
}
else {
return model[path[0]];
}
}
/**
* @param {?} controlKey
* @param {?} actualKey
* @return {?}
*/
/**
* @param {?} dest
* @param {...?} args
* @return {?}
*/
function reverseDeepMerge(dest, ...args) {
args.forEach(src => {
for (let /** @type {?} */ srcArg in src) {
if (isNullOrUndefined(dest[srcArg]) || isBlankString(dest[srcArg])) {
if (isFunction(src[srcArg])) {
dest[srcArg] = src[srcArg];
}
else {
dest[srcArg] = clone(src[srcArg]);
}
}
else if (objAndSameType(dest[srcArg], src[srcArg])) {
reverseDeepMerge(dest[srcArg], src[srcArg]);
}
}
});
return dest;
}
/**
* @param {?} value
* @return {?}
*/
function isNullOrUndefined(value) {
return value === undefined || value === null;
}
/**
* @param {?} value
* @return {?}
*/
function isUndefined(value) {
return value === undefined;
}
/**
* @param {?} value
* @return {?}
*/
function isBlankString(value) {
return value === '';
}
/**
* @param {?} value
* @return {?}
*/
function isFunction(value) {
return typeof (value) === 'function';
}
/**
* @param {?} obj1
* @param {?} obj2
* @return {?}
*/
function objAndSameType(obj1, obj2) {
return isObject(obj1) && isObject(obj2) &&
Object.getPrototypeOf(obj1) === Object.getPrototypeOf(obj2);
}
/**
* @param {?} x
* @return {?}
*/
function isObject(x) {
return x != null && typeof x === 'object';
}
/**
* @param {?} value
* @return {?}
*/
function clone(value) {
if (!isObject(value) || value instanceof RegExp || value instanceof Observable) {
return value;
}
if (Object.prototype.toString.call(value) === '[object Date]') {
return new Date(value.getTime());
}
if (Array.isArray(value)) {
return value.slice(0).map(v => clone(v));
}
value = Object.assign({}, value);
Object.keys(value).forEach(k => value[k] = clone(value[k]));
return value;
}
/**
* @param {?} expression
* @param {?} argNames
* @return {?}
*/
function evalStringExpression(expression, argNames) {
try {
return Function.bind.apply(Function, [void 0].concat(argNames.concat(`return ${expression};`)))();
}
catch (/** @type {?} */ error) {
console.error(error);
}
}
/**
* @param {?} expression
* @param {?} argNames
* @return {?}
*/
function evalExpressionValueSetter(expression, argNames) {
try {
return Function.bind
.apply(Function, [void 0].concat(argNames.concat(`${expression} = expressionValue;`)))();
}
catch (/** @type {?} */ error) {
console.error(error);
}
}
/**
* @param {?} expression
* @param {?} thisArg
* @param {?} argVal
* @return {?}
*/
function evalExpression(expression, thisArg, argVal) {
if (expression instanceof Function) {
return expression.apply(thisArg, argVal);
}
else {
return expression ? true : false;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
const FORMLY_CONFIG_TOKEN = new InjectionToken('FORMLY_CONFIG_TOKEN');
/**
* Maintains list of formly field directive types. This can be used to register new field templates.
*/
class FormlyConfig {
/**
* @param {?=} configs
*/
constructor(configs = []) {
this.types = {};
this.validators = {};
this.wrappers = {};
this.messages = {};
this.templateManipulators = {
preWrapper: [],
postWrapper: [],
};
this.extras = {
fieldTransform: undefined,
showError: function (field) {
return field.formControl && field.formControl.invalid && (field.formControl.touched || (field.options.parentForm && field.options.parentForm.submitted) || (field.field.validation && field.field.validation.show));
},
};
configs.forEach(config => this.addConfig(config));
}
/**
* @param {?} config
* @return {?}
*/
addConfig(config) {
if (config.types) {
config.types.forEach(type => this.setType(type));
}
if (config.validators) {
config.validators.forEach(validator => this.setValidator(validator));
}
if (config.wrappers) {
config.wrappers.forEach(wrapper => this.setWrapper(wrapper));
}
if (config.manipulators) {
config.manipulators.forEach(manipulator => this.setManipulator(manipulator));
}
if (config.validationMessages) {
config.validationMessages.forEach(validation => this.addValidatorMessage(validation.name, validation.message));
}
if (config.extras) {
this.extras = Object.assign({}, this.extras, config.extras);
}
}
/**
* @param {?} options
* @return {?}
*/
setType(options) {
if (Array.isArray(options)) {
options.forEach((option) => this.setType(option));
}
else {
if (!this.types[options.name]) {
this.types[options.name] = /** @type {?} */ ({});
}
this.types[options.name].component = options.component;
this.types[options.name].name = options.name;
this.types[options.name].extends = options.extends;
this.types[options.name].defaultOptions = options.defaultOptions;
if (options.wrappers) {
options.wrappers.forEach((wrapper) => this.setTypeWrapper(options.name, wrapper));
}
}
}
/**
* @param {?} name
* @return {?}
*/
getType(name) {
if (!this.types[name]) {
throw new Error(`[Formly Error] There is no type by the name of "${name}"`);
}
this.mergeExtendedType(name);
return this.types[name];
}
/**
* @param {?=} field
* @return {?}
*/
getMergedField(field = {}) {
let /** @type {?} */ name = field.type;
if (!this.types[name]) {
throw new Error(`[Formly Error] There is no type by the name of "${name}"`);
}
this.mergeExtendedType(name);
if (this.types[name].defaultOptions) {
reverseDeepMerge(field, this.types[name].defaultOptions);
}
let /** @type {?} */ extendDefaults = this.types[name].extends && this.getType(this.types[name].extends).defaultOptions;
if (extendDefaults) {
reverseDeepMerge(field, extendDefaults);
}
if (field && field.optionsTypes) {
field.optionsTypes.forEach(option => {
let /** @type {?} */ defaultOptions = this.getType(option).defaultOptions;
if (defaultOptions) {
reverseDeepMerge(field, defaultOptions);
}
});
}
if (!field.component) {
field.component = this.types[name].component;
}
if (!field.wrappers) {
field.wrappers = this.types[name].wrappers;
}
}
/**
* @param {?} options
* @return {?}
*/
setWrapper(options) {
this.wrappers[options.name] = options;
if (options.types) {
options.types.forEach((type) => {
this.setTypeWrapper(type, options.name);
});
}
}
/**
* @param {?} name
* @return {?}
*/
getWrapper(name) {
if (!this.wrappers[name]) {
throw new Error(`[Formly Error] There is no wrapper by the name of "${name}"`);
}
return this.wrappers[name];
}
/**
* @param {?} type
* @param {?} name
* @return {?}
*/
setTypeWrapper(type, name) {
if (!this.types[type]) {
this.types[type] = /** @type {?} */ ({});
}
if (!this.types[type].wrappers) {
this.types[type].wrappers = /** @type {?} */ ([]);
}
this.types[type].wrappers.push(name);
}
/**
* @param {?} options
* @return {?}
*/
setValidator(options) {
this.validators[options.name] = options;
}
/**
* @param {?} name
* @return {?}
*/
getValidator(name) {
if (!this.validators[name]) {
throw new Error(`[Formly Error] There is no validator by the name of "${name}"`);
}
return this.validators[name];
}
/**
* @param {?} name
* @param {?} message
* @return {?}
*/
addValidatorMessage(name, message) {
this.messages[name] = message;
}
/**
* @param {?} name
* @return {?}
*/
getValidatorMessage(name) {
return this.messages[name];
}
/**
* @param {?} manipulator
* @return {?}
*/
setManipulator(manipulator) {
new manipulator.class()[manipulator.method](this);
}
/**
* @param {?} name
* @return {?}
*/
mergeExtendedType(name) {
if (!this.types[name].extends) {
return;
}
const /** @type {?} */ extendedType = this.getType(this.types[name].extends);
if (!this.types[name].component) {
this.types[name].component = extendedType.component;
}
if (!this.types[name].wrappers) {
this.types[name].wrappers = extendedType.wrappers;
}
}
}
FormlyConfig.decorators = [
{ type: Injectable },
];
/** @nocollapse */
FormlyConfig.ctorParameters = () => [
{ type: Array, decorators: [{ type: Inject, args: [FORMLY_CONFIG_TOKEN,] },] },
];
/**
* @record
*/
/**
* @record
*/
/**
* @record
*/
/**
* @record
*/
/**
* @record
*/
/**
* @record
*/
/**
* @record
*/
/**
* @record
*/
/**
* @record
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* \@internal
*/
class FormlyFormExpression {
/**
* @param {?} form
* @param {?=} fields
* @param {?=} model
* @param {?=} options
* @return {?}
*/
checkFields(form, fields = [], model, options) {
this._checkFields(form, fields, model, options);
}
/**
* @param {?} form
* @param {?=} fields
* @param {?=} model
* @param {?=} options
* @return {?}
*/
_checkFields(form, fields = [], model, options) {
fields.forEach(field => {
this.checkFieldExpressionChange(form, field, this.getParentModel(model, field), options);
this.checkFieldVisibilityChange(form, field, this.getParentModel(model, field), options);
if (field.fieldGroup && field.fieldGroup.length > 0) {
this._checkFields(field.formControl ? /** @type {?} */ (field.formControl) : form, field.fieldGroup, this.getParentModel(model, field), options);
}
});
}
/**
* @param {?} form
* @param {?} field
* @param {?} model
* @param {?} options
* @return {?}
*/
checkFieldExpressionChange(form, field, model, options) {
if (!field || !field.expressionProperties) {
return;
}
const /** @type {?} */ expressionProperties = field.expressionProperties;
const /** @type {?} */ validators = FORMLY_VALIDATORS.map(v => `templateOptions.${v}`);
for (const /** @type {?} */ key in expressionProperties) {
const /** @type {?} */ expressionValue = evalExpression(expressionProperties[key].expression, { field }, [model, options.formState]);
if (expressionProperties[key].expressionValue !== expressionValue
&& (!isObject(expressionValue) || JSON.stringify(expressionValue) !== JSON.stringify(expressionProperties[key].expressionValue))) {
expressionProperties[key].expressionValue = expressionValue;
evalExpression(expressionProperties[key].expressionValueSetter, { field }, [expressionValue, model, field]);
if (key.indexOf('model.') === 0) {
const /** @type {?} */ path = key.replace(/^model\./, ''), /** @type {?} */
control = field.key && key === path ? field.formControl : form.get(path);
if (control
&& !(isNullOrUndefined(control.value) && isNullOrUndefined(expressionValue))
&& control.value !== expressionValue) {
control.patchValue(expressionValue);
}
}
if (validators.indexOf(key) !== -1 && field.formControl) {
field.formControl.updateValueAndValidity({ emitEvent: false });
}
}
}
}
/**
* @param {?} form
* @param {?} field
* @param {?} model
* @param {?} options
* @return {?}
*/
checkFieldVisibilityChange(form, field, model, options) {
if (!field || isNullOrUndefined(field.hideExpression)) {
return;
}
const /** @type {?} */ hideExpressionResult = !!evalExpression(field.hideExpression, { field }, [model, options.formState]);
if (hideExpressionResult !== field.hide) {
// toggle hide
field.hide = hideExpressionResult;
field.templateOptions.hidden = hideExpressionResult;
if (field.formControl && field.key) {
const /** @type {?} */ parent = this.fieldParentFormControl(form, field);
if (parent) {
if (hideExpressionResult === true && parent.get(/** @type {?} */ (this.fieldKey(field)))) {
this.removeFieldControl(parent, field);
}
else if (hideExpressionResult === false && !parent.get(/** @type {?} */ (this.fieldKey(field)))) {
this.addFieldControl(parent, field, model);
}
}
}
if (options.fieldChanges) {
options.fieldChanges.next(/** @type {?} */ ({ field: field, type: 'hidden', value: hideExpressionResult }));
}
}
}
/**
* @param {?} parent
* @param {?} field
* @param {?} model
* @return {?}
*/
addFieldControl(parent, field, model) {
const /** @type {?} */ fieldModel = this.getFieldModel(model, field);
if (!(isNullOrUndefined(field.formControl.value) && isNullOrUndefined(fieldModel))
&& field.formControl.value !== fieldModel) {
field.formControl.patchValue(fieldModel, { emitEvent: false });
}
if (parent instanceof FormArray) {
parent.push(field.formControl);
}
else if (parent instanceof FormGroup) {
parent.addControl(/** @type {?} */ (this.fieldKey(field)), field.formControl);
}
}
/**
* @param {?} model
* @param {?} field
* @return {?}
*/
getFieldModel(model, field) {
if (field.fieldGroup || field.fieldArray) {
return model;
}
return getFieldModel(model, field, false);
}
/**
* @param {?} model
* @param {?} field
* @return {?}
*/
getParentModel(model, field) {
if (field.key && (field.fieldGroup || field.fieldArray)) {
return getFieldModel(model, field, true);
}
return model;
}
/**
* @param {?} parent
* @param {?} field
* @return {?}
*/
removeFieldControl(parent, field) {
if (parent instanceof FormArray) {
parent.removeAt(/** @type {?} */ (this.fieldKey(field)));
}
else if (parent instanceof FormGroup) {
parent.removeControl(/** @type {?} */ (this.fieldKey(field)));
}
}
/**
* @param {?} form
* @param {?} field
* @return {?}
*/
fieldParentFormControl(form, field) {
const /** @type {?} */ paths = getKeyPath(field);
paths.pop(); // remove last path
return /** @type {?} */ ((paths.length > 0 ? form.get(paths) : form));
}
/**
* @param {?} field
* @return {?}
*/
fieldKey(field) {
return getKeyPath(field).pop();
}
}
FormlyFormExpression.decorators = [
{ type: Injectable },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class FormlyFormBuilder {
/**
* @param {?} formlyConfig
* @param {?} formlyFormExpression
*/
constructor(formlyConfig, formlyFormExpression) {
this.formlyConfig = formlyConfig;
this.formlyFormExpression = formlyFormExpression;
this.formId = 0;
}
/**
* @param {?} form
* @param {?=} fields
* @param {?=} model
* @param {?=} options
* @return {?}
*/
buildForm(form, fields = [], model, options) {
let /** @type {?} */ fieldTransforms = (options && options.fieldTransform) || this.formlyConfig.extras.fieldTransform;
if (!Array.isArray(fieldTransforms)) {
fieldTransforms = [fieldTransforms];
}
fieldTransforms.forEach(fieldTransform => {
if (fieldTransform) {
fields = fieldTransform(fields, model, form, options);
if (!fields) {
throw new Error('fieldTransform must return an array of fields');
}
}
});
assignModelToFields(fields, model);
this._buildForm(form, fields, options);
this.formlyFormExpression.checkFields(form, fields, model, options);
}
/**
* @param {?} form
* @param {?=} fields
* @param {?=} options
* @return {?}
*/
_buildForm(form, fields = [], options) {
this.formId++;
this.registerFormControls(form, fields, options);
}
/**
* @param {?} form
* @param {?} fields
* @param {?} options
* @return {?}
*/
registerFormControls(form, fields, options) {
fields.forEach((field, index) => {
field.id = getFieldId(`formly_${this.formId}`, field, index);
this.initFieldOptions(field);
this.initFieldExpression(field, options);
this.initFieldValidation(field);
this.initFieldWrappers(field);
this.initFieldAsyncValidation(field);
if (field.key && field.type) {
const /** @type {?} */ paths = getKeyPath({ key: field.key });
let /** @type {?} */ rootForm = form, /** @type {?} */ rootModel = field.model;
paths.forEach((path, index) => {
// FormGroup/FormArray only allow string value for path
const /** @type {?} */ formPath = path.toString();
// is last item
if (index === paths.length - 1) {
this.addFormControl(rootForm, field, rootModel, formPath);
if (field.fieldArray) {
field.fieldGroup = [];
field.model.forEach((m, i) => field.fieldGroup.push(Object.assign({}, clone(field.fieldArray), { key: `${i}` })));
assignModelToFields(field.fieldGroup, rootModel);
}
}
else {
let /** @type {?} */ nestedForm = /** @type {?} */ (rootForm.get(formPath));
if (!nestedForm) {
nestedForm = new FormGroup({});
this.addControl(rootForm, formPath, nestedForm);
}
if (!rootModel[path]) {
rootModel[path] = typeof path === 'string' ? {} : [];
}
rootForm = nestedForm;
rootModel = rootModel[path];
}
});
}
if (field.fieldGroup) {
if (!field.type) {
field.type = 'formly-group';
}
if (field.key) {
this.addFormControl(form, field, { [field.key]: field.fieldArray ? [] : {} }, field.key);
this._buildForm(/** @type {?} */ (field.formControl), field.fieldGroup, options);
}
else {
// if `hideExpression` is set in that case we have to deal
// with toggle FormControl for each field in fieldGroup separately
if (field.hideExpression) {
field.fieldGroup.forEach(f => {
let /** @type {?} */ hideExpression = f.hideExpression || (() => false);
if (typeof hideExpression === 'string') {
hideExpression = evalStringExpression(hideExpression, ['model', 'formState']);
}
f.hideExpression = (model, formState) => field.hide || hideExpression(model, formState);
});
}
this._buildForm(form, field.fieldGroup, options);
}
}
});
}
/**
* @param {?} field
* @param {?} options
* @return {?}
*/
initFieldExpression(field, options) {
if (field.expressionProperties) {
for (const /** @type {?} */ key in /** @type {?} */ (field.expressionProperties)) {
if (typeof field.expressionProperties[key] === 'string' || isFunction(field.expressionProperties[key])) {
// cache built expression
field.expressionProperties[key] = {
expression: isFunction(field.expressionProperties[key]) ? field.expressionProperties[key] : evalStringExpression(field.expressionProperties[key], ['model', 'formState']),
expressionValueSetter: evalExpressionValueSetter(`field.${key}`, ['expressionValue', 'model', 'field']),
};
}
}
}
if (field.hideExpression) {
// delete hide value in order to force re-evaluate it in FormlyFormExpression.
delete field.hide;
if (typeof field.hideExpression === 'string') {
// cache built expression
field.hideExpression = evalStringExpression(field.hideExpression, ['model', 'formState']);
}
}
}
/**
* @param {?} field
* @return {?}
*/
initFieldOptions(field) {
field.templateOptions = field.templateOptions || {};
if (field.type) {
this.formlyConfig.getMergedField(field);
if (field.key) {
field.templateOptions = Object.assign({
label: '',
placeholder: '',
focus: false,
}, field.templateOptions);
}
}
}
/**
* @param {?} field
* @return {?}
*/
initFieldAsyncValidation(field) {
const /** @type {?} */ validators = [];
if (field.asyncValidators) {
for (const /** @type {?} */ validatorName in field.asyncValidators) {
if (validatorName !== 'validation') {
let /** @type {?} */ validator = field.asyncValidators[validatorName];
if (isObject(validator)) {
validator = validator.expression;
}
validators.push((control) => new Promise((resolve) => {
return validator(control, field).then((result) => {
resolve(result ? null : { [validatorName]: true });
});
}));
}
}
}
if (field.asyncValidators && Array.isArray(field.asyncValidators.validation)) {
field.asyncValidators.validation
.forEach((validator) => validators.push(this.wrapNgValidatorFn(field, validator)));
}
if (validators.length) {
if (field.asyncValidators && !Array.isArray(field.asyncValidators.validation)) {
field.asyncValidators.validation = Validators.composeAsync([field.asyncValidators.validation, ...validators]);
}
else {
field.asyncValidators = {
validation: Validators.composeAsync(validators),
};
}
}
}
/**
* @param {?} field
* @return {?}
*/
initFieldValidation(field) {
const /** @type {?} */ validators = [];
FORMLY_VALIDATORS
.filter(opt => (field.templateOptions && field.templateOptions.hasOwnProperty(opt))
|| (field.expressionProperties && field.expressionProperties[`templateOptions.${opt}`]))
.forEach((opt) => {
validators.push((control) => {
if (field.templateOptions[opt] === false) {
return null;
}
return this.getValidation(opt, field.templateOptions[opt])(control);
});
});
if (field.validators) {
for (const /** @type {?} */ validatorName in field.validators) {
if (validatorName !== 'validation') {
let /** @type {?} */ validator = field.validators[validatorName];
if (isObject(validator)) {
validator = validator.expression;
}
validators.push((control) => validator(control, field) ? null : { [validatorName]: true });
}
}
}
if (field.validators && Array.isArray(field.validators.validation)) {
field.validators.validation
.forEach((validator) => validators.push(this.wrapNgValidatorFn(field, validator)));
}
if (validators.length) {
if (field.validators && !Array.isArray(field.validators.validation)) {
field.validators.validation = Validators.compose([field.validators.validation, ...validators]);
}
else {
field.validators = {
validation: Validators.compose(validators),
};
}
}
}
/**
* @param {?} form
* @param {?} field
* @param {?} model
* @param {?} path
* @return {?}
*/
addFormControl(form, field, model, path) {
let /** @type {?} */ control;
const /** @type {?} */ validators = field.validators ? field.validators.validation : undefined, /** @type {?} */
asyncValidators = field.asyncValidators ? field.asyncValidators.validation : undefined, /** @type {?} */
updateOn = field.modelOptions && field.modelOptions.updateOn ?
field.modelOptions.updateOn : undefined;
const /** @type {?} */ abstractControlOptions = /** @type {?} */ ({
validators,
asyncValidators,
updateOn,
});
if (field.formControl instanceof AbstractControl || form.get(path)) {
control = field.formControl || form.get(path);
if (!(isNullOrUndefined(control.value) && isNullOrUndefined(model[path]))
&& control.value !== model[path]
&& control instanceof FormControl) {
control.patchValue(model[path]);
}
}
else if (field.component && field.component.createControl) {
control = field.component.createControl(model[path], field);
}
else if (field.fieldGroup && field.key && field.key === path && !field.fieldArray) {
control = new FormGroup(model[path], abstractControlOptions);
}
else if (field.fieldArray && field.key && field.key === path) {
control = new FormArray([], abstractControlOptions);
}
else {
control = new FormControl(model[path], abstractControlOptions);
}
if (field.templateOptions.disabled) {
control.disable();
}
// Replace decorated property with a getter that returns the observable.
// https://github.com/angular-redux/store/blob/master/src/decorators/select.ts#L79-L85
if (delete field.templateOptions.disabled) {
Object.defineProperty(field.templateOptions, 'disabled', {
get: (function () { return !this.formControl.enabled; }).bind(field),
set: (function (value) {
if (this.expressionProperties && this.expressionProperties.hasOwnProperty('templateOptions.disabled')) {
this.expressionProperties['templateOptions.disabled'].expressionValue = value;
}
value ? this.formControl.disable() : this.formControl.enable();
}).bind(field),
enumerable: true,
configurable: true,
});
}
this.addControl(form, path, control, field);
}
/**
* @param {?} form
* @param {?} key
* @param {?} formControl
* @param {?=} field
* @return {?}
*/
addControl(form, key, formControl, field) {
if (field) {
field.formControl = formControl;
}
if (form instanceof FormArray) {
if (form.at(/** @type {?} */ (key)) !== formControl) {
form.setControl(/** @type {?} */ (key), formControl);
}
}
else {
if (form.get(/** @type {?} */ (key)) !== formControl) {
form.setControl(/** @type {?} */ (key), formControl);
}
}
}
/**
* @param {?} opt
* @param {?} value
* @return {?}
*/
getValidation(opt, value) {
switch (opt) {
case 'required':
return Validators.required;
case 'pattern':
return Validators.pattern(value);
case 'minLength':
return Validators.minLength(value);
case 'maxLength':
return Validators.maxLength(value);
case 'min':
return Validators.min(value);
case 'max':
return Validators.max(value);
}
}
/**
* @param {?} field
* @param {?} validator
* @return {?}
*/
wrapNgValidatorFn(field, validator) {
validator = typeof validator === 'string'
? this.formlyConfig.getValidator(validator).validation
: validator;
return (control) => (/** @type {?} */ (validator))(control, field);
}
/**
* @param {?} field
* @return {?}
*/
initFieldWrappers(field) {
const /** @type {?} */ templateManipulators = {
preWrapper: [],
postWrapper: [],
};
if (field.templateOptions) {
this.mergeTemplateManipulators(templateManipulators, field.templateOptions.templateManipulators);
}
this.mergeTemplateManipulators(templateManipulators, this.formlyConfig.templateManipulators);
const /** @type {?} */ preWrappers = templateManipulators.preWrapper.map(m => m(field)).filter(type => type), /** @type {?} */
postWrappers = templateManipulators.postWrapper.map(m => m(field)).filter(type => type);
if (!field.wrappers) {
field.wrappers = [];
}
field.wrappers = [...preWrappers, ...(field.wrappers || []), ...postWrappers];
}
/**
* @param {?} source
* @param {?} target
* @return {?}
*/
mergeTemplateManipulators(source, target) {
target = target || {};
if (target.preWrapper) {
source.preWrapper = source.preWrapper.concat(target.preWrapper);
}
if (target.postWrapper) {
source.postWrapper = source.postWrapper.concat(target.postWrapper);
}
return source;
}
}
FormlyFormBuilder.decorators = [
{ type: Injectable },
];
/** @nocollapse */
FormlyFormBuilder.ctorParameters = () => [
{ type: FormlyConfig, },
{ type: FormlyFormExpression, },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class FormlyForm {
/**
* @param {?} formlyBuilder
* @param {?} formlyExpression
* @param {?} formlyConfig
* @param {?} parentForm
* @param {?} parentFormGroup
* @param {?} parentFormlyForm
*/
constructor(formlyBuilder, formlyExpression, formlyConfig, parentForm, parentFormGroup, parentFormlyForm) {
this.formlyBuilder = formlyBuilder;
this.formlyExpression = formlyExpression;
this.formlyConfig = formlyConfig;
this.parentForm = parentForm;
this.parentFormGroup = parentFormGroup;
this.parentFormlyForm = parentFormlyForm;
this.model = {};
this.form = new FormGroup({});
this.fields = [];
this.modelChange = new EventEmitter();
this.modelWillChange = new EventEmitter();
this.modelDidChange = new EventEmitter();
/**
* \@internal
*/
this.isRoot = true;
this.modelChangeSubs = [];
}
/**
* @return {?}
*/
ngDoCheck() {
this.checkExpressionChange();
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (!this.fields || this.fields.length === 0 || !this.isRoot) {
return;
}
if (changes["fields"] || changes["form"]) {
this.modelWillChange.emit();
this.model = this.model || {};
this.form = this.form || (new FormGroup({}));
this.setOptions();
this.clearModelSubscriptions();
(/** @type {?} */ (this.options)).components = [];
this.formlyBuilder.buildForm(this.form, this.fields, this.model, this.options);
this.trackModelChanges(this.fields);
this.updateInitialValue();
this.modelDidChange.emit();
}
else if (changes["model"]) {
this.modelWillChange.emit();
this.patchModel(this.model);
this.modelDidChange.emit();
}
}
/**
* @return {?}
*/
ngOnDestroy() {
this.clearModelSubscriptions();
}
/**
* @param {?} event
* @return {?}
*/
changeModel(event) {
assignModelValue(this.model, event.key, event.value);
this.modelChange.emit(this.model);
this.checkExpressionChange();
}
/**
* @return {?}
*/
setOptions() {
this.options = this.options || {};
this.options.formState = this.options.formState || {};
if (!this.options.showError) {
this.options.showError = this.formlyConfig.extras.showError;
}
if (!this.options.fieldChanges) {
this.options.fieldChanges = new Subject();
}
if (!this.options.resetModel) {
this.options.resetModel = this.resetModel.bind(this);
}
if (!this.options.parentForm) {
this.options.parentForm = this.parentFormGroup || this.parentForm;
}
if (!this.options.updateInitialValue) {
this.options.updateInitialValue = this.updateInitialValue.bind(this);
}
if (!(/** @type {?} */ (this.options)).resetTrackModelChanges) {
(/** @type {?} */ (this.options)).resetTrackModelChanges = () => {
this.clearModelSubscriptions();
this.trackModelChanges(this.fields);
};
}
}
/**
* @return {?}
*/
checkExpressionChange() {
if (this.isRoot) {
this.formlyExpression.checkFields(this.form, this.fields, this.model, this.options);
}
}
/**
* @param {?} fields
* @param {?=} rootKey
* @return {?}
*/
trackModelChanges(fields, rootKey = []) {
fields.forEach(field => {
if (field.key && field.type && !field.fieldGroup && !field.fieldArray) {
const /** @type {?} */ valueChanges = field.formControl.valueChanges.pipe(field.modelOptions && field.modelOptions.debounce && field.modelOptions.debounce.default
? debounceTime(field.modelOptions.debounce.default)
: tap(() => { }), map(value => {
if (field.parsers && field.parsers.length > 0) {
field.parsers.forEach(parserFn => value = parserFn(value));
}
return value;
}), tap(value => this.changeModel({ key: [...rootKey, field.key].join('.'), value })));
this.modelChangeSubs.push(valueChanges.subscribe());
}
if (field.fieldGroup && field.fieldGroup.length > 0) {
this.trackModelChanges(field.fieldGroup, field.key ? [...rootKey, field.key] : rootKey);
}
});
}
/**
* @return {?}
*/
clearModelSubscriptions() {
this.modelChangeSubs.forEach(sub => sub.unsubscribe());
this.modelChangeSubs = [];
}
/**
* @param {?} model
* @return {?}
*/
patchModel(model) {
this.clearModelSubscriptions();
this.resetFieldArray(this.fields, model);
this.initializeFormValue(this.form);
(/** @type {?} */ (this.form)).patchValue(model, { onlySelf: true });
this.trackModelChanges(this.fields);
}
/**
* @param {?=} model
* @return {?}
*/
resetModel(model) {
this.modelWillChange.emit();
(/** @type {?} */ (this.options)).components.forEach(component => {
if (component.onBeforePatchValue) {
component.onBeforePatchValue();
}
});
model = isNullOrUndefined(model) ? this.initialModel : model;
this.resetFieldArray(this.fields, model);
// we should call `NgForm::resetForm` to ensure changing `submitted` state after resetting form
// but only when the current component is a root one.
if (!this.parentFormlyForm && this.options.parentForm && this.options.parentForm.control === this.form) {
this.options.parentForm.resetForm(model);
}
else {
this.form.reset(model);
}
this.modelDidChange.emit();
}
/**
* @param {?} fields
* @param {?} newModel
* @return {?}
*/
resetFieldArray(fields, newModel) {
fields.forEach(field => {
if ((field.fieldGroup && field.fieldGroup.length > 0) || field.fieldArray) {
const /** @type {?} */ newFieldModel = getFieldModel(newModel, field, true);
if (field.fieldArray) {
field.fieldGroup = field.fieldGroup || [];
field.fieldGroup.length = 0;
if (field.model !== newFieldModel && field.model) {
field.model.length = 0;
}
const /** @type {?} */ formControl = /** @type {?} */ (field.formControl);
while (formControl.length !== 0) {
formControl.removeAt(0);
}
newFieldModel.forEach((m, i) => {
field.model[i] = m;
field.fieldGroup.push(Object.assign({}, clone(field.fieldArray), { key: `${i}` }));
this.formlyBuilder.buildForm(formControl, [field.fieldGroup[i]], newFieldModel, this.options);
});
}
else {
this.resetFieldArray(field.fieldGroup, newFieldModel);
}
}
else if (field.key && field.type) {
field.formControl.reset(getFieldModel(newModel, field, false));
}
});
}
/**
* @param {?} control
* @return {?}
*/
initializeFormValue(control) {
if (control instanceof FormControl) {
control.setValue(null);
}
else if (control instanceof FormGroup) {
Object.keys(control.controls).forEach(k => this.initializeFormValue(control.controls[k]));
}
else if (control instanceof FormArray) {
control.controls.forEach(c => this.initializeFormValue(c));
}
}
/**
* @return {?}
*/
updateInitialValue() {
this.initialModel = reverseDeepMerge({}, this.model);
}
}
FormlyForm.decorators = [
{ type: Component, args: [{
selector: 'formly-form',
template: `
<formly-field *ngFor="let field of fields"
[model]="field.model" [form]="form"
[field]="field"
[ngClass]="field.className"
[options]="options">
</formly-field>
<ng-content></ng-content>
`,
},] },
];
/** @nocollapse */
FormlyForm.ctorParameters = () => [
{ type: FormlyFormBuilder, },
{ type: FormlyFormExpression, },
{ type: FormlyConfig, },
{ type: NgForm, decorators: [{ type: Optional },] },
{ type: FormGroupDirective, decorators: [{ type: Optional },] },
{ type: FormlyForm, decorators: [{ type: Optional }, { type: SkipSelf },] },
];
FormlyForm.propDecorators = {
"model": [{ type: Input },],
"form": [{ type: Input },],
"fields": [{ type: Input },],
"options": [{ type: Input },],
"modelChange": [{ type: Output },],
"modelWillChange": [{ type: Output },],
"modelDidChange": [{ type: Output },],
"isRoot": [{ type: Input },],
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class FormlyField {
/**
* @param {?} formlyConfig
* @param {?} componentFactoryResolver
*/
constructor(formlyConfig, componentFactoryResolver) {
this.formlyConfig = formlyConfig;
this.componentFactoryResolver = componentFactoryResolver;
this.options = {};
this.modelChange = new EventEmitter();
this.componentRefs = [];
}
/**
* @return {?}
*/
ngAfterContentInit() {
this.lifeCycleHooks(this.lifecycle.afterContentInit);
}
/**
* @return {?}
*/
ngAfterContentChecked() {
this.lifeCycleHooks(this.lifecycle.afterContentChecked);
}
/**
* @return {?}
*/
ngAfterViewInit() {
this.lifeCycleHooks(this.lifecycle.afterViewInit);
}
/**
* @return {?}
*/
ngAfterViewChecked() {
this.lifeCycleHooks(this.lifecycle.afterViewChecked);
}
/**
* @return {?}
*/
ngDoCheck() {
this.lifeCycleHooks(this.lifecycle.doCheck);
}
/**
* @return {?}
*/
ngOnInit() {
if (!this.field.template) {
this.createFieldComponent();
}
this.lifeCycleHooks(this.lifecycle.onInit);
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
this.lifeCycleHooks(this.lifecycle.onChanges);
this.componentRefs.forEach(ref => {
Object.assign(ref.instance, {
model: this.model,
form: this.form,
field: this.field,
options: this.options,
});
});
}
/**
* @return {?}
*/
ngOnDestroy() {
this.lifeCycleHooks(this.lifecycle.onDestroy);
this.componentRefs.forEach(componentRef => componentRef.destroy());
this.componentRefs = [];
}
/**
* @return {?}
*/
createFieldComponent() {
const /** @type {?} */ type = this.formlyConfig.getType(this.field.type);
let /** @type {?} */ fieldComponent = this.fieldComponent;
(this.field.wrappers || []).forEach(wrapperName => {
const /** @type {?} */ wrapperRef = this.createComponent(fieldComponent, this.formlyConfig.getWrapper(wrapperName).component);
fieldComponent = wrapperRef.instance.fieldComponent;
});
return this.createComponent(fieldComponent, type.component);
}
/**
* @param {?} fieldComponent
* @param {?} component
* @return {?}
*/
createComponent(fieldComponent, component) {
let /** @type {?} */ componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
let /** @type {?} */ ref = /** @type {?} */ (fieldComponent.createComponent(componentFactory));
Object.assign(ref.instance, {
model: this.model,
form: this.form,
field: this.field,
options: this.options,
});
const /** @type {?} */ optionsComponents = (/** @type {?} */ (this.options)).components;
if (optionsComponents) {
optionsComponents.push(ref.instance);
}
this.componentRefs.push(ref);
return ref;
}
/**
* @return {?}
*/
get lifecycle() {
return this.field.lifecycle || {};
}
/**
* @param {?} callback
* @return {?}
*/
lifeCycleHooks(callback) {
if (callback) {
callback(this.form, this.field, this.model, this.options);
}
}
}
FormlyField.decorators = [
{ type: Component, args: [{
selector: 'formly-field',
template: `
<ng-template #fieldComponent></ng-template>
<div *ngIf="field.template && !field.fieldGroup" [innerHtml]="field.template"></div>
`,
host: {
'[style.display]': 'field.hide ? "none":""',
},
},] },
];
/** @nocollapse */
FormlyField.ctorParameters = () => [
{ type: FormlyConfig, },
{ type: ComponentFactoryResolver, },
];
FormlyField.propDecorators = {
"model": [{ type: Input },],
"form": [{ type: Input },],
"field": [{ type: Input },],
"options": [{ type: Input },],
"modelChange": [{ type: Output },],
"fieldComponent": [{ type: ViewChild, args: ['fieldComponent', { read: ViewContainerRef },] },],
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class FormlyAttributes {
/**
* @param {?} renderer
* @param {?} elementRef
*/
constructor(renderer, elementRef) {
this.renderer = renderer;
this.elementRef = elementRef;
this.attributes = ['id', 'name', 'placeholder', 'tabindex', 'step', 'readonly'];
this.statements = ['change', 'keydown', 'keyup', 'keypress', 'click', 'focus', 'blur'];
}
/**
* @return {?}
*/
onFocus() {
this.field.focus = true;
}
/**
* @return {?}
*/
onBlur() {
this.field.focus = false;
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (changes["field"]) {
const /** @type {?} */ fieldChanges = changes["field"];
this.attributes
.filter(attr => this.canApplyRender(fieldChanges, attr))
.forEach(attr => this.renderer.setAttribute(this.elementRef.nativeElement, attr, this.getPropValue(this.field, attr)));
if (this.field.templateOptions && this.field.templateOptions.attributes) {
const /** @type {?} */ attributes = this.field.templateOptions.attributes;
Object.keys(attributes).forEach(name => this.renderer.setAttribute(this.elementRef.nativeElement, name, /** @type {?} */ (attributes[name])));
}
this.statements
.filter(statement => this.canApplyRender(fieldChanges, statement))
.forEach(statement => this.renderer.listen(this.elementRef.nativeElement, statement, this.getStatementValue(statement)));
if ((fieldChanges.previousValue || {}).focus !== (fieldChanges.currentValue || {}).focus && this.elementRef.nativeElement.focus) {
this.elementRef.nativeElement[this.field.focus ? 'focus' : 'blur']();
}
}
}
/**
* @param {?} field
* @param {?} prop
* @return {?}
*/
getPropValue(field, prop) {
field = field || {};
if (field.templateOptions && field.templateOptions[prop]) {
return field.templateOptions[prop];
}
return (/** @type {?} */ (field))[prop] || '';
}
/**
* @param {?} statement
* @return {?}
*/
getStatementValue(statement) {
const /** @type {?} */ fn = this.field.templateOptions[statement];
return (event) => fn(this.field, event);
}
/**
* @param {?} fieldChange
* @param {?} prop
* @return {?}
*/
canApplyRender(fieldChange, prop) {
const /** @type {?} */ currentValue = this.getPropValue(this.field, prop), /** @type {?} */
previousValue = this.getPropValue(fieldChange.previousValue, prop);
if (previousValue !== currentValue) {
if (this.statements.indexOf(prop) !== -1) {
return typeof currentValue === 'function';
}
return true;
}
return false;
}
}
FormlyAttributes.decorators = [
{ type: Directive, args: [{
selector: '[formlyAttributes]',
},] },
];
/** @nocollapse */
FormlyAttributes.ctorParameters = () => [
{ type: Renderer2, },
{ type: ElementRef, },
];
FormlyAttributes.propDecorators = {
"field": [{ type: Input, args: ['formlyAttributes',] },],
"onFocus": [{ type: HostListener, args: ['focus',] },],
"onBlur": [{ type: HostListener, args: ['blur',] },],
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @abstract
*/
class Field {
/**
* @return {?}
*/
get key() { return this.field.key; }
/**
* @return {?}
*/
get formControl() { return this.field.formControl; }
/**
* @return {?}
*/
get to() { return this.field.templateOptions; }
/**
* @return {?}
*/
get showError() { return this.options.showError(this); }
/**
* @return {?}
*/
get id() { return this.field.id; }
/**
* @return {?}
*/
get formState() { return this.options.formState || {}; }
/**
* @return {?}
*/
onBeforePatchValue() { }
}
Field.propDecorators = {
"form": [{ type: Input },],
"field": [{ type: Input },],
"model": [{ type: Input },],
"options": [{ type: Input },],
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @abstract
*/
class FieldType extends Field {
/**
* @return {?}
*/
ngOnInit() { }
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) { }
/**
* @return {?}
*/
ngDoCheck() { }
/**
* @return {?}
*/
ngAfterContentInit() { }
/**
* @return {?}
*/
ngAfterContentChecked() { }
/**
* @return {?}
*/
ngAfterViewInit() { }
/**
* @return {?}
*/
ngAfterViewChecked() { }
/**
* @return {?}
*/
ngOnDestroy() { }
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @abstract
*/
class FieldArrayType extends FieldType {
/**
* @param {?} builder
*/
constructor(builder) {
super();
this.builder = builder;
}
/**
* @param {?=} i
* @param {?=} initialModel
* @return {?}
*/
add(i, initialModel) {
i = isNullOrUndefined(i) ? this.field.fieldGroup.length : i;
this.model.splice(i, 0, initialModel ? clone(initialModel) : undefined);
this.field.fieldGroup.splice(i, 0, Object.assign({}, clone(this.field.fieldArray)));
this.field.fieldGroup.forEach((field, index) => {
field.key = `${index}`;
});
const /** @type {?} */ form = new FormArray([]);
this.builder.buildForm(form, [this.field.fieldGroup[i]], this.model, this.options);
this.formControl.insert(i, form.at(0));
(/** @type {?} */ (this.options)).resetTrackModelChanges();
}
/**
* @param {?} i
* @return {?}
*/
remove(i) {
this.formControl.removeAt(i);
this.field.fieldGroup.splice(i, 1);
this.field.fieldGroup.forEach((f, index) => f.key = `${index}`);
this.model.splice(i, 1);
(/** @type {?} */ (this.options)).resetTrackModelChanges();
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @abstract
*/
class FieldWrapper extends Field {
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class FormlyGroup extends FieldType {
}
FormlyGroup.decorators = [
{ type: Component, args: [{
selector: 'formly-group',
template: `
<formly-form
[fields]="field.fieldGroup"
[isRoot]="false"
[model]="model"
[form]="field.formControl || form"
[options]="options"
[ngClass]="field.fieldGroupClassName">
<ng-content></ng-content>
</formly-form>
`,
},] },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class FormlyValidationMessage {
/**
* @param {?} formlyConfig
*/
constructor(formlyConfig) {
this.formlyConfig = formlyConfig;
}
/**
* @param {?} control
* @return {?}
*/
set fieldForm(control) {
console.warn(`formly-validation-message: Passing 'fieldForm' input is deprecated and it will be removed in the 4.0 version.`);
}
/**
* @return {?}
*/
get errorMessage() {
const /** @type {?} */ fieldForm = this.field.formControl;
for (let /** @type {?} */ error in fieldForm.errors) {
if (fieldForm.errors.hasOwnProperty(error)) {
let /** @type {?} */ message = this.formlyConfig.getValidatorMessage(error);
if (this.field.validation && this.field.validation.messages && this.field.validation.messages[error]) {
message = this.field.validation.messages[error];
}
if (this.field.validators && this.field.validators[error] && this.field.validators[error].message) {
message = this.field.validators[error].message;
}
if (this.field.asyncValidators && this.field.asyncValidators[error] && this.field.asyncValidators[error].message) {
message = this.field.asyncValidators[error].message;
}
if (typeof message === 'function') {
return message(fieldForm.errors[error], this.field);
}
return message;
}
}
}
}
FormlyValidationMessage.decorators = [
{ type: Component, args: [{
selector: 'formly-validation-message',
template: `{{ errorMessage }}`,
},] },
];
/** @nocollapse */
FormlyValidationMessage.ctorParameters = () => [
{ type: FormlyConfig, },
];
FormlyValidationMessage.propDecorators = {
"field": [{ type: Input },],
"fieldForm": [{ type: Input },],
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class FormlyModule {
/**
* @param {?=} config
* @return {?}
*/
static forRoot(config = {}) {
return {
ngModule: FormlyModule,
providers: [
FormlyFormBuilder,
FormlyFormExpression,
FormlyConfig,
{ provide: FORMLY_CONFIG_TOKEN, useValue: { types: [{ name: 'formly-group', component: FormlyGroup }] }, multi: true },
{ provide: FORMLY_CONFIG_TOKEN, useValue: config, multi: true },
{ provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: config, multi: true },
],
};
}
/**
* @param {?=} config
* @return {?}
*/
static forChild(config = {}) {
return {
ngModule: FormlyModule,
providers: [
{ provide: FORMLY_CONFIG_TOKEN, useValue: config, multi: true },
{ provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: config, multi: true },
],
};
}
}
FormlyModule.decorators = [
{ type: NgModule, args: [{
declarations: [FormlyForm, FormlyField, FormlyAttributes, FormlyGroup, FormlyValidationMessage],
entryComponents: [FormlyGroup],
exports: [FormlyForm, FormlyField, FormlyAttributes, FormlyGroup, FormlyValidationMessage],
imports: [
CommonModule,
ReactiveFormsModule,
],
},] },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/*
* Public API Surface of core
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Generated bundle index. Do not edit.
*/
export { FormlyForm, FormlyField, FormlyAttributes, FormlyConfig, FormlyFormBuilder, Field, FieldType, FieldArrayType, FieldWrapper, FormlyModule, FormlyGroup as ɵc, FORMLY_CONFIG_TOKEN as ɵa, FormlyFormExpression as ɵb, FormlyValidationMessage as ɵd };
//# sourceMappingURL=wfw-ngx-formly.js.map