import { Component, OnChanges, Input, SimpleChanges } from '@angular/core';
import { AbstractControl, FormControl, FormGroup, FormArray } from '@angular/forms';
import { FormlyValueChangeEvent } from './../services/formly.event.emitter';
import { FormlyFieldConfig } from './formly.field.config';
import { FormlyFormBuilder } from '../services/formly.form.builder';
import { assignModelValue, isNullOrUndefined, isObject, reverseDeepMerge, getKey, getValueForKey, getFieldModel } from '../utils';
@Component({
selector: 'formly-form',
template: `
`,
})
export class FormlyForm implements OnChanges {
@Input() model: any = {};
@Input() form: FormGroup = new FormGroup({});
@Input() fields: FormlyFieldConfig[] = [];
@Input() options: any;
private initialModel: any;
constructor(private formlyBuilder: FormlyFormBuilder) {}
ngOnChanges(changes: SimpleChanges) {
if (changes['fields']) {
this.model = this.model || {};
this.form = this.form || (new FormGroup({}));
this.setOptions();
this.formlyBuilder.buildForm(this.form, this.fields, this.model, this.options);
this.updateInitialValue();
} else if (changes['model'] && this.fields && this.fields.length > 0) {
this.form.patchValue(this.model);
}
}
fieldModel(field: FormlyFieldConfig) {
if (field.key && (field.fieldGroup || field.fieldArray)) {
return getFieldModel(this.model, field, true);
}
return this.model;
}
changeModel(event: FormlyValueChangeEvent) {
assignModelValue(this.model, event.key, event.value);
}
setOptions() {
this.options = this.options || {};
this.options.resetModel = this.resetModel.bind(this);
this.options.updateInitialValue = this.updateInitialValue.bind(this);
}
private resetModel(model?: any) {
model = isNullOrUndefined(model) ? this.initialModel : model;
this.form.patchValue(model);
this.resetFormGroup(model, this.form);
this.resetFormModel(model, this.model);
}
private resetFormModel(model: any, formModel: any, path?: (string | number)[]) {
if (!isObject(model) && !Array.isArray(model)) {
return;
}
// removes
for (let key in formModel) {
if (!(key in model) || isNullOrUndefined(model[key])) {
if (!this.form.get((path || []).concat(key))) {
// don't remove if bound to a control
delete formModel[key];
}
}
}
// inserts and updates
for (let key in model) {
if (!isNullOrUndefined(model[key])) {
if (key in formModel) {
this.resetFormModel(model[key], formModel[key], (path || []).concat(key));
}
else {
formModel[key] = model[key];
}
}
}
}
private resetFormGroup(model: any, form: FormGroup, actualKey?: string) {
for (let controlKey in form.controls) {
let key = getKey(controlKey, actualKey);
if (form.controls[controlKey] instanceof FormGroup) {
this.resetFormGroup(model, form.controls[controlKey], key);
}
if (form.controls[controlKey] instanceof FormArray) {
this.resetArray(model, form.controls[controlKey], key);
}
if (form.controls[controlKey] instanceof FormControl) {
form.controls[controlKey].setValue(getValueForKey(model, key));
}
}
}
private resetArray(model: any, formArray: FormArray, key: string) {
let newValue = getValueForKey(model, key);
// removes and updates
for (let i = formArray.controls.length - 1; i >= 0; i--) {
if (formArray.controls[i] instanceof FormGroup) {
if (newValue && !isNullOrUndefined(newValue[i])) {
this.resetFormGroup(newValue[i], formArray.controls[i]);
}
else {
formArray.removeAt(i);
let value = getValueForKey(this.model, key);
if (Array.isArray(value)) {
value.splice(i, 1);
}
}
}
}
// inserts
if (Array.isArray(newValue) && formArray.controls.length < newValue.length) {
let remaining = newValue.length - formArray.controls.length;
let initialLength = formArray.controls.length;
for (let i = 0; i < remaining; i++) {
let pos = initialLength + i;
getValueForKey(this.model, key).push(newValue[pos]);
formArray.controls.push(new FormGroup({}));
}
}
}
private updateInitialValue() {
let obj = reverseDeepMerge(this.form.value, this.model);
this.initialModel = JSON.parse(JSON.stringify(obj));
}
}