Browse Source

Support rules in components. (#752)

* Support rules in components.

* Performance improvements.

* Smaller forms class.

* Bugfixes.
pull/755/head
Sebastian Stehle 4 years ago
committed by GitHub
parent
commit
c0107aecd6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      frontend/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.html
  2. 12
      frontend/app/framework/utils/rxjs-extensions.ts
  3. 12
      frontend/app/framework/utils/types.ts
  4. 161
      frontend/app/shared/state/contents.forms-helpers.ts
  5. 50
      frontend/app/shared/state/contents.forms.spec.ts
  6. 245
      frontend/app/shared/state/contents.forms.ts

2
frontend/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.html

@ -8,7 +8,7 @@
{{ 'schemas.rules.empty' | sqxTranslate }}
</div>
<div class="form-group row" *ngFor="let form of editForm.rulesControls; let i = index" [formGroup]="form">
<div class="form-group row gx-2" *ngFor="let form of editForm.rulesControls; let i = index" [formGroup]="form">
<div class="col col-action">
<sqx-control-errors for="action"></sqx-control-errors>

12
frontend/app/framework/utils/rxjs-extensions.ts

@ -6,7 +6,7 @@
*/
import { EMPTY, Observable, ReplaySubject, throwError } from 'rxjs';
import { catchError, distinctUntilChanged, filter, map, onErrorResumeNext, share, switchMap } from 'rxjs/operators';
import { catchError, debounceTime, distinctUntilChanged, filter, map, onErrorResumeNext, share, switchMap } from 'rxjs/operators';
import { DialogService } from './../services/dialog.service';
import { Version, versioned, Versioned } from './version';
@ -51,6 +51,16 @@ export function shareMapSubscribed<T, R = T>(dialogs: DialogService, project: (v
};
}
export function debounceTimeSafe<T>(duration: number) {
return function mapOperation(source: Observable<T>) {
if (duration > 0) {
return source.pipe(debounceTime(duration), onErrorResumeNext());
} else {
return source.pipe(onErrorResumeNext());
}
};
}
export function defined<T>() {
return function mapOperation(source: Observable<T | undefined | null>): Observable<T> {
return source.pipe(filter(x => !!x), map(x => x!), distinctUntilChanged());

12
frontend/app/framework/utils/types.ts

@ -103,6 +103,18 @@ export module Types {
return Types.isUndefined(value) === true || Types.isNull(value) === true;
}
export function fastMerge<T>(lhs: ReadonlyArray<T>, rhs: ReadonlyArray<T>) {
if (rhs.length === 0) {
return lhs;
}
if (lhs.length === 0) {
return rhs;
}
return [...lhs, ...rhs];
}
export function clone<T>(lhs: T): T {
const any: any = lhs;

161
frontend/app/shared/state/contents.forms-helpers.ts

@ -5,9 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
/* eslint-disable @typescript-eslint/no-implied-eval */
/* eslint-disable no-useless-return */
import { AbstractControl, ValidatorFn } from '@angular/forms';
import { Types } from '@app/framework';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AppLanguageDto } from './../services/app-languages.service';
@ -36,6 +38,32 @@ export abstract class Hidden {
}
}
export function groupFields<T extends FieldDto>(fields: ReadonlyArray<T>): { separator?: T; fields: ReadonlyArray<T> }[] {
const result: { separator?: T; fields: ReadonlyArray<T> }[] = [];
let currentSeparator: T | undefined;
let currentFields: T[] = [];
for (const field of fields) {
if (field.properties.isContentField) {
currentFields.push(field);
} else {
if (currentFields.length > 0) {
result.push({ separator: currentSeparator, fields: currentFields });
}
currentFields = [];
currentSeparator = field;
}
}
if (currentFields.length > 0) {
result.push({ separator: currentSeparator, fields: currentFields });
}
return result;
}
export class FieldSection<TSeparator, TChild extends { hidden: boolean }> extends Hidden {
constructor(
public readonly separator: TSeparator | undefined,
@ -78,7 +106,7 @@ export class PartitionConfig {
}
}
type RuleContext = { data: any; itemData?: any; user?: any };
type RuleContext = { data: any; user?: any };
export class CompiledRule {
private readonly function: Function;
@ -93,18 +121,20 @@ export class CompiledRule {
constructor(
private readonly rule: FieldRule,
private readonly useItemData: boolean,
) {
try {
// eslint-disable-next-line @typescript-eslint/no-implied-eval
this.function = new Function(`return function(user, ctx, data, itemData) { return ${rule.condition} }`)();
} catch {
this.function = () => false;
}
}
public eval(context: RuleContext) {
public eval(context: RuleContext, itemData: any) {
try {
return this.function(context.user, context, context.data, context.itemData);
const data = this.useItemData ? itemData || context.data : context.data;
return this.function(context.user, context, data, itemData);
} catch {
return false;
}
@ -118,16 +148,102 @@ export type AbstractContentFormState = {
};
export interface FormGlobals {
allRules: ReadonlyArray<CompiledRule>;
partitions: PartitionConfig;
schema: SchemaDto;
schemas: { [id: string ]: SchemaDto };
remoteValidator?: ValidatorFn;
}
const EMPTY_RULES: CompiledRule[] = [];
export interface RulesProvider {
compileRules(schema: SchemaDto | undefined): ReadonlyArray<CompiledRule>;
setSchema(schema?: SchemaDto): void;
getRules(form: AbstractContentForm<any, any>): ReadonlyArray<CompiledRule>;
}
export class ComponentRulesProvider implements RulesProvider {
private schema?: SchemaDto;
constructor(
private readonly parentPath: string,
private readonly parent: RulesProvider,
) {
}
public setSchema(schema?: SchemaDto) {
this.schema = schema;
}
public compileRules(schema: SchemaDto | undefined): ReadonlyArray<CompiledRule> {
return this.parent.compileRules(schema);
}
public getRules(form: AbstractContentForm<any, any>) {
return Types.fastMerge(this.parent.getRules(form), this.getRelativeRules(form));
}
private getRelativeRules(form: AbstractContentForm<any, any>) {
const rules = this.compileRules(this.schema);
if (rules.length === 0) {
return EMPTY_RULES;
}
const pathField = form.fieldPath.substr(this.parentPath.length + 1);
const pathSimplified = pathField.replace('.iv.', '.');
return rules.filter(x => x.field === pathField || x.field === pathSimplified);
}
}
export class RootRulesProvider implements RulesProvider {
private readonly compiledRules: { [id: string]: ReadonlyArray<CompiledRule> } = {};
private readonly rules: ReadonlyArray<CompiledRule>;
constructor(schema: SchemaDto) {
this.rules = schema.fieldRules.map(x => new CompiledRule(x, false));
}
public setSchema() {
return;
}
public compileRules(schema: SchemaDto | undefined) {
if (!schema) {
return EMPTY_RULES;
}
let result = this.compileRules[schema.id];
if (!result) {
result = schema.fieldRules.map(x => new CompiledRule(x, true));
this.compiledRules[schema.id] = result;
}
return result;
}
public getRules(form: AbstractContentForm<any, any>) {
const rules = this.rules;
if (rules.length === 0) {
return EMPTY_RULES;
}
const pathField = form.fieldPath;
const pathSimplified = pathField.replace('.iv.', '.');
return rules.filter(x => x.field === pathField || x.field === pathSimplified);
}
}
export abstract class AbstractContentForm<T extends FieldDto, TForm extends AbstractControl> extends Hidden {
private readonly disabled$ = new BehaviorSubject<boolean>(false);
private readonly rules: ReadonlyArray<CompiledRule>;
private readonly currentRules: ReadonlyArray<CompiledRule>;
public get disabled() {
return this.disabled$.value;
@ -139,35 +255,36 @@ export abstract class AbstractContentForm<T extends FieldDto, TForm extends Abst
protected constructor(
public readonly globals: FormGlobals,
public readonly fieldPath: string,
public readonly field: T,
public readonly fieldPath: string,
public readonly form: TForm,
public readonly isOptional: boolean,
public readonly rules: RulesProvider,
) {
super();
const simplifiedPath = fieldPath.replace('.iv.', '.');
this.currentRules = rules.getRules(this);
}
this.rules = globals.allRules.filter(x => x.field === fieldPath || x.field === simplifiedPath);
public path(relative: string) {
return `${this.fieldPath}.${relative}`;
}
public updateState(context: RuleContext, parentState: AbstractContentFormState) {
public updateState(context: RuleContext, fieldData: any, itemData: any, parentState: AbstractContentFormState) {
const state = {
isDisabled: this.field.isDisabled || parentState.isDisabled === true,
isHidden: parentState.isHidden === true,
isRequired: this.field.properties.isRequired && !this.isOptional,
};
if (this.rules) {
for (const rule of this.rules) {
if (rule.eval(context)) {
if (rule.action === 'Disable') {
state.isDisabled = true;
} else if (rule.action === 'Hide') {
state.isHidden = true;
} else {
state.isRequired = true;
}
for (const rule of this.currentRules) {
if (rule.eval(context, itemData)) {
if (rule.action === 'Disable') {
state.isDisabled = true;
} else if (rule.action === 'Hide') {
state.isHidden = true;
} else {
state.isRequired = true;
}
}
}
@ -182,14 +299,14 @@ export abstract class AbstractContentForm<T extends FieldDto, TForm extends Abst
}
}
this.updateCustomState(context, state);
this.updateCustomState(context, fieldData, itemData, state);
}
public unset() {
this.form.setValue(undefined);
}
protected updateCustomState(_context: RuleContext, _state: AbstractContentFormState): void {
protected updateCustomState(_context: RuleContext, _fieldData: any, _itemData: any, _state: AbstractContentFormState): void {
return;
}

50
frontend/app/shared/state/contents.forms.spec.ts

@ -8,8 +8,9 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { AbstractControl, FormArray } from '@angular/forms';
import { MathHelper } from '@app/framework';
import { AppLanguageDto, createProperties, EditContentForm, getContentValue, HtmlValue, LanguageDto, RootFieldDto } from '@app/shared/internal';
import { FieldRule } from './../services/schemas.service';
import { FieldRule, SchemaDto } from './../services/schemas.service';
import { FieldArrayForm } from './contents.forms';
import { PartitionConfig } from './contents.forms-helpers';
import { TestValues } from './_test-helpers';
@ -458,6 +459,49 @@ describe('ContentForm', () => {
expect(array.get(1)!.get('nested42')!.hidden).toBeFalsy();
});
it('should hide components fields based on condition', () => {
const componentId = MathHelper.guid();
const component = createSchema({
fields: [
createField({
id: 1,
properties: createProperties('String'),
partitioning: 'invariant',
}),
],
fieldRules: [{
field: 'field1', action: 'Hide', condition: 'data.field1 > 100',
}],
});
const contentForm = createForm([
createField({
id: 4,
properties: createProperties('Components'),
partitioning: 'invariant',
}),
], [], {
[componentId]: component,
});
const array = contentForm.get(complexSchema.fields[3])!.get(languages[0]) as FieldArrayForm;
contentForm.load({
field4: {
iv: [{
schemaId: componentId,
field1: 120,
}, {
schemaId: componentId,
field1: 99,
}],
},
});
expect(array.get(0)!.get('field1')!.hidden).toBeTruthy();
expect(array.get(1)!.get('field1')!.hidden).toBeFalsy();
});
it('should load with array and not enable disabled nested fields', () => {
const { contentForm, array } = createArrayFormWith2Items();
@ -690,8 +734,8 @@ describe('ContentForm', () => {
});
});
function createForm(fields: RootFieldDto[], fieldRules: FieldRule[] = []) {
function createForm(fields: RootFieldDto[], fieldRules: FieldRule[] = [], schemas: { [id: string]: SchemaDto } = {}) {
return new EditContentForm(languages,
createSchema({ fields, fieldRules }), {}, {}, 0);
createSchema({ fields, fieldRules }), schemas, {}, 0);
}
});

245
frontend/app/shared/state/contents.forms.ts

@ -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;
}
}
if (currentFields.length > 0) {
sections.push(new FieldSection<RootFieldDto, FieldForm>(currentSeparator, currentFields));
}
this.sections = sections;
forms.push(childForm);
let change$ = valueAll$(this.form);
this.fields[field.name] = childForm;
}
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);
}
}

Loading…
Cancel
Save