|
|
|
@ -6,14 +6,13 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; |
|
|
|
import { Form, getRawValue, Types, UndefinableFormArray, UndefinableFormGroup, valueAll$ } from '@app/framework'; |
|
|
|
import { debounceTimeSafe, Form, getRawValue, Types, UndefinableFormArray, UndefinableFormGroup, valueAll$ } from '@app/framework'; |
|
|
|
import { BehaviorSubject, Observable } from 'rxjs'; |
|
|
|
import { debounceTime, onErrorResumeNext } from 'rxjs/operators'; |
|
|
|
import { AppLanguageDto } from './../services/app-languages.service'; |
|
|
|
import { LanguageDto } from './../services/languages.service'; |
|
|
|
import { FieldDto, RootFieldDto, SchemaDto, TableField } from './../services/schemas.service'; |
|
|
|
import { ComponentFieldPropertiesDto, fieldInvariant } from './../services/schemas.types'; |
|
|
|
import { AbstractContentForm, AbstractContentFormState, CompiledRule, FieldSection, FormGlobals, PartitionConfig } from './contents.forms-helpers'; |
|
|
|
import { AbstractContentForm, AbstractContentFormState, ComponentRulesProvider, FieldSection, FormGlobals, groupFields, PartitionConfig, RootRulesProvider, RulesProvider } from './contents.forms-helpers'; |
|
|
|
import { FieldDefaultValue, FieldsValidators } from './contents.forms.visitors'; |
|
|
|
|
|
|
|
type SaveQueryFormType = { name: string; user: boolean }; |
|
|
|
@ -93,53 +92,36 @@ export class EditContentForm extends Form<FormGroup, any> { |
|
|
|
super(new FormGroup({})); |
|
|
|
|
|
|
|
const globals: FormGlobals = { |
|
|
|
allRules: schema.fieldRules.map(x => new CompiledRule(x)), |
|
|
|
schema, |
|
|
|
schemas, |
|
|
|
partitions: new PartitionConfig(languages), |
|
|
|
remoteValidator: this.remoteValidator, |
|
|
|
}; |
|
|
|
|
|
|
|
const sections: FieldSection<RootFieldDto, FieldForm>[] = []; |
|
|
|
const rules = new RootRulesProvider(schema); |
|
|
|
|
|
|
|
let currentSeparator: RootFieldDto | undefined; |
|
|
|
let currentFields: FieldForm[] = []; |
|
|
|
this.sections = groupFields(schema.fields).map(({ separator, fields }) => { |
|
|
|
const forms: FieldForm[] = []; |
|
|
|
|
|
|
|
for (const field of schema.fields) { |
|
|
|
if (field.properties.isContentField) { |
|
|
|
const childPath = field.name; |
|
|
|
const childForm = new FieldForm(globals, childPath, field); |
|
|
|
|
|
|
|
currentFields.push(childForm); |
|
|
|
|
|
|
|
this.fields[field.name] = childForm; |
|
|
|
for (const field of fields) { |
|
|
|
const childForm = |
|
|
|
new FieldForm( |
|
|
|
globals, |
|
|
|
field, |
|
|
|
field.name, |
|
|
|
rules); |
|
|
|
|
|
|
|
this.form.setControl(field.name, childForm.form); |
|
|
|
} else { |
|
|
|
if (currentFields.length > 0) { |
|
|
|
sections.push(new FieldSection<RootFieldDto, FieldForm>(currentSeparator, currentFields)); |
|
|
|
} |
|
|
|
|
|
|
|
currentFields = []; |
|
|
|
currentSeparator = field; |
|
|
|
} |
|
|
|
} |
|
|
|
forms.push(childForm); |
|
|
|
|
|
|
|
if (currentFields.length > 0) { |
|
|
|
sections.push(new FieldSection<RootFieldDto, FieldForm>(currentSeparator, currentFields)); |
|
|
|
this.fields[field.name] = childForm; |
|
|
|
} |
|
|
|
|
|
|
|
this.sections = sections; |
|
|
|
|
|
|
|
let change$ = valueAll$(this.form); |
|
|
|
|
|
|
|
if (debounce > 0) { |
|
|
|
change$ = change$.pipe(debounceTime(debounce), onErrorResumeNext()); |
|
|
|
} else { |
|
|
|
change$ = change$.pipe(onErrorResumeNext()); |
|
|
|
} |
|
|
|
return new FieldSection<RootFieldDto, FieldForm>(separator, forms); |
|
|
|
}); |
|
|
|
|
|
|
|
change$.subscribe(value => { |
|
|
|
valueAll$(this.form).pipe(debounceTimeSafe(debounce)).subscribe(value => { |
|
|
|
this.valueChange$.next(value); |
|
|
|
|
|
|
|
this.updateState(value); |
|
|
|
@ -202,7 +184,7 @@ export class EditContentForm extends Form<FormGroup, any> { |
|
|
|
const context = { ...this.context || {}, data }; |
|
|
|
|
|
|
|
for (const field of Object.values(this.fields)) { |
|
|
|
field.updateState(context, { isDisabled: this.form.disabled }); |
|
|
|
field.updateState(context, data[field.field.name], data, { isDisabled: this.form.disabled }); |
|
|
|
} |
|
|
|
|
|
|
|
for (const section of this.sections) { |
|
|
|
@ -219,12 +201,23 @@ export class FieldForm extends AbstractContentForm<RootFieldDto, FormGroup> { |
|
|
|
private readonly partitions: { [partition: string]: FieldItemForm } = {}; |
|
|
|
private isRequired: boolean; |
|
|
|
|
|
|
|
constructor(globals: FormGlobals, fieldPath: string, field: RootFieldDto) { |
|
|
|
super(globals, fieldPath, field, FieldForm.buildForm(), false); |
|
|
|
constructor( |
|
|
|
globals: FormGlobals, |
|
|
|
field: RootFieldDto, |
|
|
|
fieldPath: string, |
|
|
|
rules: RulesProvider, |
|
|
|
) { |
|
|
|
super(globals, field, fieldPath, FieldForm.buildForm(), false, rules); |
|
|
|
|
|
|
|
for (const { key, isOptional } of globals.partitions.getAll(field)) { |
|
|
|
const childPath = `${fieldPath}.${key}`; |
|
|
|
const childForm = buildForm(this.globals, childPath, field, isOptional, key); |
|
|
|
const childForm = |
|
|
|
buildForm( |
|
|
|
this.globals, |
|
|
|
field, |
|
|
|
this.path(key), |
|
|
|
isOptional, |
|
|
|
rules, |
|
|
|
key); |
|
|
|
|
|
|
|
this.partitions[key] = childForm; |
|
|
|
|
|
|
|
@ -263,7 +256,13 @@ export class FieldForm extends AbstractContentForm<RootFieldDto, FormGroup> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected updateCustomState(context: any, state: AbstractContentFormState) { |
|
|
|
public prepareLoad(value: any) { |
|
|
|
for (const key of Object.keys(this.partitions)) { |
|
|
|
this.partitions[key].prepareLoad(value?.[key]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected updateCustomState(context: any, fieldData: any, itemData: any, state: AbstractContentFormState) { |
|
|
|
const isRequired = state.isRequired === true; |
|
|
|
|
|
|
|
if (this.isRequired !== isRequired) { |
|
|
|
@ -289,14 +288,8 @@ export class FieldForm extends AbstractContentForm<RootFieldDto, FormGroup> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for (const partition of Object.values(this.partitions)) { |
|
|
|
partition.updateState(context, state); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public prepareLoad(value: any) { |
|
|
|
for (const key of Object.keys(this.partitions)) { |
|
|
|
this.partitions[key].prepareLoad(value?.[key]); |
|
|
|
this.partitions[key].updateState(context, fieldData?.[key], itemData, state); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -308,18 +301,22 @@ export class FieldForm extends AbstractContentForm<RootFieldDto, FormGroup> { |
|
|
|
export class FieldValueForm extends AbstractContentForm<FieldDto, FormControl> { |
|
|
|
private isRequired = false; |
|
|
|
|
|
|
|
constructor(globals: FormGlobals, path: string, field: FieldDto, |
|
|
|
isOptional: boolean, partition: string, |
|
|
|
constructor( |
|
|
|
globals: FormGlobals, |
|
|
|
field: FieldDto, |
|
|
|
fieldPath: string, |
|
|
|
isOptional: boolean, |
|
|
|
rules: RulesProvider, |
|
|
|
partition: string, |
|
|
|
) { |
|
|
|
super(globals, path, field, |
|
|
|
super(globals, field, fieldPath, |
|
|
|
FieldValueForm.buildControl(field, isOptional, partition, globals), |
|
|
|
isOptional, |
|
|
|
); |
|
|
|
isOptional, rules); |
|
|
|
|
|
|
|
this.isRequired = field.properties.isRequired && !isOptional; |
|
|
|
} |
|
|
|
|
|
|
|
protected updateCustomState(_: any, state: AbstractContentFormState) { |
|
|
|
protected updateCustomState(_context: any, _fieldData: any, _itemData: any, state: AbstractContentFormState) { |
|
|
|
const isRequired = state.isRequired === true; |
|
|
|
|
|
|
|
if (!this.isOptional && this.isRequired !== isRequired) { |
|
|
|
@ -366,14 +363,18 @@ export class FieldArrayForm extends AbstractContentForm<FieldDto, UndefinableFor |
|
|
|
this.item$.next(value); |
|
|
|
} |
|
|
|
|
|
|
|
constructor(globals: FormGlobals, path: string, field: FieldDto, isOptional: boolean, |
|
|
|
constructor( |
|
|
|
globals: FormGlobals, |
|
|
|
field: FieldDto, |
|
|
|
fieldPath: string, |
|
|
|
isOptional: boolean, |
|
|
|
rules: RulesProvider, |
|
|
|
private readonly partition: string, |
|
|
|
private readonly isComponents: boolean, |
|
|
|
) { |
|
|
|
super(globals, path, field, |
|
|
|
super(globals, field, fieldPath, |
|
|
|
FieldArrayForm.buildControl(field, isOptional), |
|
|
|
isOptional, |
|
|
|
); |
|
|
|
isOptional, rules); |
|
|
|
} |
|
|
|
|
|
|
|
public get(index: number) { |
|
|
|
@ -382,13 +383,13 @@ export class FieldArrayForm extends AbstractContentForm<FieldDto, UndefinableFor |
|
|
|
|
|
|
|
public addCopy(source: ObjectForm) { |
|
|
|
if (this.isComponents) { |
|
|
|
const child = new ComponentForm(this.globals, this.fieldPath, this.field as RootFieldDto, this.isOptional, this.partition); |
|
|
|
const child = this.createComponent(); |
|
|
|
|
|
|
|
child.load(getRawValue(source.form)); |
|
|
|
|
|
|
|
this.addChild(child); |
|
|
|
} else { |
|
|
|
const child = new ArrayItemForm(this.globals, this.fieldPath, this.field as RootFieldDto, this.isOptional, this.partition); |
|
|
|
const child = this.createItem(); |
|
|
|
|
|
|
|
child.load(getRawValue(source.form)); |
|
|
|
|
|
|
|
@ -397,13 +398,13 @@ export class FieldArrayForm extends AbstractContentForm<FieldDto, UndefinableFor |
|
|
|
} |
|
|
|
|
|
|
|
public addComponent(schemaId?: string) { |
|
|
|
const child = new ComponentForm(this.globals, this.fieldPath, this.field, this.isOptional, this.partition, schemaId); |
|
|
|
const child = this.createComponent(schemaId); |
|
|
|
|
|
|
|
this.addChild(child); |
|
|
|
} |
|
|
|
|
|
|
|
public addItem() { |
|
|
|
const child = new ArrayItemForm(this.globals, this.fieldPath, this.field as RootFieldDto, this.isOptional, this.partition); |
|
|
|
const child = this.createItem(); |
|
|
|
|
|
|
|
this.addChild(child); |
|
|
|
} |
|
|
|
@ -451,12 +452,6 @@ export class FieldArrayForm extends AbstractContentForm<FieldDto, UndefinableFor |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected updateCustomState(context: any, state: AbstractContentFormState) { |
|
|
|
for (const item of this.items) { |
|
|
|
item.updateState(context, state); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public prepareLoad(value: any) { |
|
|
|
if (Types.isArray(value)) { |
|
|
|
while (this.items.length < value.length) { |
|
|
|
@ -477,6 +472,33 @@ export class FieldArrayForm extends AbstractContentForm<FieldDto, UndefinableFor |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected updateCustomState(context: any, fieldData: any, itemData: any, state: AbstractContentFormState) { |
|
|
|
for (let i = 0; i < this.items.length; i++) { |
|
|
|
this.items[i].updateState(context, fieldData?.[i], itemData, state); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private createItem() { |
|
|
|
return new ArrayItemForm( |
|
|
|
this.globals, |
|
|
|
this.field as RootFieldDto, |
|
|
|
this.fieldPath, |
|
|
|
this.isOptional, |
|
|
|
this.rules, |
|
|
|
this.partition); |
|
|
|
} |
|
|
|
|
|
|
|
private createComponent(schemaId?: string) { |
|
|
|
return new ComponentForm( |
|
|
|
this.globals, |
|
|
|
this.field as RootFieldDto, |
|
|
|
this.fieldPath, |
|
|
|
this.isOptional, |
|
|
|
this.rules, |
|
|
|
this.partition, |
|
|
|
schemaId); |
|
|
|
} |
|
|
|
|
|
|
|
private static buildControl(field: FieldDto, isOptional: boolean) { |
|
|
|
const validators = FieldsValidators.create(field, isOptional); |
|
|
|
|
|
|
|
@ -494,10 +516,17 @@ export class ObjectForm<TField extends FieldDto = FieldDto> extends AbstractCont |
|
|
|
return this.fieldSections; |
|
|
|
} |
|
|
|
|
|
|
|
constructor(globals: FormGlobals, path: string, field: TField, isOptional: boolean, |
|
|
|
constructor( |
|
|
|
globals: FormGlobals, |
|
|
|
field: TField, |
|
|
|
fieldPath: string, |
|
|
|
isOptional: boolean, |
|
|
|
rules: RulesProvider, |
|
|
|
private readonly partition: string, |
|
|
|
) { |
|
|
|
super(globals, path, field, ObjectForm.buildControl(field, isOptional, false), isOptional); |
|
|
|
super(globals, field, fieldPath, |
|
|
|
ObjectForm.buildControl(field, isOptional, false), |
|
|
|
isOptional, rules); |
|
|
|
} |
|
|
|
|
|
|
|
public get(field: string | { name: string }): FieldItemForm | undefined { |
|
|
|
@ -515,31 +544,27 @@ export class ObjectForm<TField extends FieldDto = FieldDto> extends AbstractCont |
|
|
|
if (schema) { |
|
|
|
this.form.reset({}); |
|
|
|
|
|
|
|
let currentSeparator: FieldDto | undefined; |
|
|
|
let currentFields: FieldItemForm[] = []; |
|
|
|
for (const { separator, fields } of groupFields(schema)) { |
|
|
|
const forms: FieldItemForm[] = []; |
|
|
|
|
|
|
|
for (const field of schema) { |
|
|
|
if (field.properties.isContentField) { |
|
|
|
const childPath = `${this.fieldPath}.${field.name}`; |
|
|
|
const childForm = buildForm(this.globals, childPath, field, this.isOptional, this.partition); |
|
|
|
for (const field of fields) { |
|
|
|
const childForm = |
|
|
|
buildForm( |
|
|
|
this.globals, |
|
|
|
field, |
|
|
|
this.path(field.name), |
|
|
|
this.isOptional, |
|
|
|
this.rules, |
|
|
|
this.partition); |
|
|
|
|
|
|
|
this.form.setControl(field.name, childForm.form); |
|
|
|
|
|
|
|
currentFields.push(childForm); |
|
|
|
forms.push(childForm); |
|
|
|
|
|
|
|
this.fields[field.name] = childForm; |
|
|
|
} else { |
|
|
|
if (currentFields.length > 0) { |
|
|
|
this.fieldSections.push(new FieldSection<FieldDto, FieldItemForm>(currentSeparator, currentFields)); |
|
|
|
} |
|
|
|
|
|
|
|
currentFields = []; |
|
|
|
currentSeparator = field; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (currentFields.length > 0) { |
|
|
|
this.fieldSections.push(new FieldSection<FieldDto, FieldItemForm>(currentSeparator, currentFields)); |
|
|
|
this.fieldSections.push(new FieldSection<FieldDto, FieldItemForm>(separator, forms)); |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.form.reset(undefined); |
|
|
|
@ -558,11 +583,9 @@ export class ObjectForm<TField extends FieldDto = FieldDto> extends AbstractCont |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected updateCustomState(context: any, state: AbstractContentFormState) { |
|
|
|
const itemData = this.form.getRawValue(); |
|
|
|
|
|
|
|
for (const field of Object.values(this.fields)) { |
|
|
|
field.updateState({ ...context, itemData }, state); |
|
|
|
protected updateCustomState(context: any, fieldData: any, _: any, state: AbstractContentFormState) { |
|
|
|
for (const key of Object.keys(this.fields)) { |
|
|
|
this.fields[key].updateState(context, fieldData?.[key], fieldData, state); |
|
|
|
} |
|
|
|
|
|
|
|
for (const section of this.sections) { |
|
|
|
@ -582,8 +605,15 @@ export class ObjectForm<TField extends FieldDto = FieldDto> extends AbstractCont |
|
|
|
} |
|
|
|
|
|
|
|
export class ArrayItemForm extends ObjectForm<RootFieldDto> { |
|
|
|
constructor(globals: FormGlobals, path: string, field: RootFieldDto, isOptional: boolean, partition: string) { |
|
|
|
super(globals, path, field, isOptional, partition); |
|
|
|
constructor( |
|
|
|
globals: FormGlobals, |
|
|
|
field: RootFieldDto, |
|
|
|
fieldPath: string, |
|
|
|
isOptional: boolean, |
|
|
|
rules: RulesProvider, |
|
|
|
partition: string, |
|
|
|
) { |
|
|
|
super(globals, field, fieldPath, isOptional, rules, partition); |
|
|
|
|
|
|
|
this.init(field.nested); |
|
|
|
} |
|
|
|
@ -598,8 +628,17 @@ export class ComponentForm extends ObjectForm { |
|
|
|
return this.globals.schemas[this.schemaId!]; |
|
|
|
} |
|
|
|
|
|
|
|
constructor(globals: FormGlobals, path: string, field: FieldDto, isOptional: boolean, partition: string, schemaId?: string) { |
|
|
|
super(globals, path, field, isOptional, partition); |
|
|
|
constructor( |
|
|
|
globals: FormGlobals, |
|
|
|
field: FieldDto, |
|
|
|
fieldPath: string, |
|
|
|
isOptional: boolean, |
|
|
|
rules: RulesProvider, |
|
|
|
partition: string, |
|
|
|
schemaId?: string, |
|
|
|
) { |
|
|
|
super(globals, field, fieldPath, isOptional, |
|
|
|
new ComponentRulesProvider(fieldPath, rules), partition); |
|
|
|
|
|
|
|
this.properties = field.properties as ComponentFieldPropertiesDto; |
|
|
|
|
|
|
|
@ -613,6 +652,8 @@ export class ComponentForm extends ObjectForm { |
|
|
|
this.schemaId = schemaId; |
|
|
|
|
|
|
|
if (this.schema) { |
|
|
|
this.rules.setSchema(this.schema); |
|
|
|
|
|
|
|
this.init(this.schema.fields); |
|
|
|
|
|
|
|
this.form.setControl('schemaId', new FormControl(schemaId)); |
|
|
|
@ -635,15 +676,15 @@ export class ComponentForm extends ObjectForm { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function buildForm(globals: FormGlobals, path: string, field: FieldDto, isOptional: boolean, partition: string) { |
|
|
|
function buildForm(globals: FormGlobals, field: FieldDto, fieldPath: string, isOptional: boolean, rules: RulesProvider, partition: string) { |
|
|
|
switch (field.properties.fieldType) { |
|
|
|
case 'Array': |
|
|
|
return new FieldArrayForm(globals, path, field, isOptional, partition, false); |
|
|
|
return new FieldArrayForm(globals, field, fieldPath, isOptional, rules, partition, false); |
|
|
|
case 'Component': |
|
|
|
return new ComponentForm(globals, path, field, isOptional, partition); |
|
|
|
return new ComponentForm(globals, field, fieldPath, isOptional, rules, partition); |
|
|
|
case 'Components': |
|
|
|
return new FieldArrayForm(globals, path, field, isOptional, partition, true); |
|
|
|
return new FieldArrayForm(globals, field, fieldPath, isOptional, rules, partition, true); |
|
|
|
default: |
|
|
|
return new FieldValueForm(globals, path, field, isOptional, partition); |
|
|
|
return new FieldValueForm(globals, field, fieldPath, isOptional, rules, partition); |
|
|
|
} |
|
|
|
} |
|
|
|
|