+ [emitChange]="false"
+ (dfBlur)="onBlur($event)"
+ (dfFocus)="onFocus($event)">
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts
index 7ad62ce479..2d5f33eac0 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts
@@ -11,12 +11,7 @@ import {
} from '@angular/core';
import { Observable } from 'rxjs/Observable';
-import {
- DynamicFormControlEvent,
- DynamicFormControlModel,
- DynamicFormGroupModel,
- DynamicInputModel
-} from '@ng-dynamic-forms/core';
+import { DynamicFormControlModel, DynamicFormGroupModel, DynamicInputModel } from '@ng-dynamic-forms/core';
import { isEqual } from 'lodash';
import { DynamicGroupModel, PLACEHOLDER_PARENT_METADATA } from './dynamic-group.model';
@@ -25,7 +20,6 @@ import { SubmissionFormsModel } from '../../../../../../core/shared/config/confi
import { FormService } from '../../../../form.service';
import { FormComponent } from '../../../../form.component';
import { Chips } from '../../../../../chips/models/chips.model';
-import { DynamicLookupModel } from '../lookup/dynamic-lookup.model';
import { hasValue, isEmpty, isNotEmpty } from '../../../../../empty.util';
import { shrinkInOut } from '../../../../../animations/shrink';
import { ChipsItem } from '../../../../../chips/models/chips-item.model';
@@ -48,6 +42,7 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
@Input() formId: string;
@Input() group: FormGroup;
@Input() model: DynamicGroupModel;
+ @Input() showErrorMessages = false;
@Output() blur: EventEmitter
= new EventEmitter();
@Output() change: EventEmitter = new EventEmitter();
@@ -57,7 +52,6 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
public formCollapsed = Observable.of(false);
public formModel: DynamicFormControlModel[];
public editMode = false;
- public invalid = false;
private selectedChipItem: ChipsItem;
private subs: Subscription[] = [];
@@ -72,7 +66,7 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
ngOnInit() {
const config = {rows: this.model.formConfiguration} as SubmissionFormsModel;
- if (isNotEmpty(this.model.value)) {
+ if (!this.model.isEmpty()) {
this.formCollapsed = Observable.of(true);
}
this.model.valueUpdates.subscribe((value: any[]) => {
@@ -80,38 +74,22 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
});
this.formId = this.formService.getUniqueId(this.model.id);
- this.formModel = this.formBuilderService.modelFromConfiguration(config, this.model.scopeUUID, {});
- this.chips = new Chips(this.model.value, 'value', this.model.mandatoryField);
+ this.formModel = this.formBuilderService.modelFromConfiguration(config, this.model.scopeUUID, {}, this.model.submissionScope, this.model.readOnly);
+ const initChipsValue = this.model.isEmpty() ? [] : this.model.value;
+ this.chips = new Chips(initChipsValue, 'value', this.model.mandatoryField);
this.subs.push(
this.chips.chipsItems
.subscribe((subItems: any[]) => {
const items = this.chips.getChipsItems();
// Does not emit change if model value is equal to the current value
if (!isEqual(items, this.model.value)) {
- if (isEmpty(items)) {
- // If items is empty, last element has been removed
- // so emit an empty value that allows to dispatch
- // a remove JSON PATCH operation
- const emptyItem = Object.create({});
- Object.keys(this.model.value[0])
- .forEach((key) => {
- emptyItem[key] = null;
- });
- items.push(emptyItem);
+ // if ((isNotEmpty(items) && !this.model.isEmpty()) || (isEmpty(items) && !this.model.isEmpty())) {
+ if (!(isEmpty(items) && this.model.isEmpty())) {
+ this.model.valueUpdates.next(items);
+ this.change.emit();
}
-
- this.model.valueUpdates.next(items);
- this.change.emit();
}
}),
- // Invalid state for year
- this.group.get(this.model.id).statusChanges.subscribe((state) => {
- if (state === 'INVALID') {
- this.invalid = true;
- } else {
- this.invalid = false;
- }
- })
)
}
@@ -130,8 +108,8 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
return res;
}
- onChange(event: DynamicFormControlEvent) {
- return
+ onBlur(event) {
+ this.blur.emit();
}
onChipSelected(event) {
@@ -142,26 +120,23 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
modelRow.group.forEach((model: DynamicInputModel) => {
const value = (this.selectedChipItem.item[model.name] === PLACEHOLDER_PARENT_METADATA
|| this.selectedChipItem.item[model.name].value === PLACEHOLDER_PARENT_METADATA)
- ? null
- : this.selectedChipItem.item[model.name];
+ ? null
+ : this.selectedChipItem.item[model.name];
if (value instanceof FormFieldMetadataValueObject || value instanceof AuthorityValueModel) {
model.valueUpdates.next(value.display);
} else {
model.valueUpdates.next(value);
}
- // if (model instanceof DynamicLookupModel) {
- // (model as DynamicLookupModel).valueUpdates.next(value);
- // } else if (model instanceof DynamicInputModel) {
- // model.valueUpdates.next(value);
- // } else {
- // (model as any).value = value;
- // }
});
});
this.editMode = true;
}
+ onFocus(event) {
+ this.focus.emit(event);
+ }
+
collapseForm() {
this.formCollapsed = Observable.of(true);
this.clear();
@@ -178,7 +153,9 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
this.editMode = false;
}
this.resetForm();
- // this.change.emit(event);
+ if (!this.model.isEmpty()) {
+ this.formCollapsed = Observable.of(true);
+ }
}
save() {
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts
index 4ec029661b..59e442b4e9 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts
@@ -1,6 +1,8 @@
import { DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core';
import { FormRowModel } from '../../../../../../core/shared/config/config-submission-forms.model';
import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model';
+import { AuthorityValueModel } from '../../../../../../core/integration/models/authority-value.model';
+import { isEmpty, isNull } from '../../../../../empty.util';
export const DYNAMIC_FORM_CONTROL_TYPE_RELATION = 'RELATION';
export const PLACEHOLDER_PARENT_METADATA = '#PLACEHOLDER_PARENT_METADATA_VALUE#';
@@ -14,6 +16,7 @@ export interface DynamicGroupModelConfig extends DsDynamicInputModelConfig {
name: string,
relationFields: string[],
scopeUUID: string,
+ submissionScope: string;
value?: any;
}
@@ -25,7 +28,8 @@ export class DynamicGroupModel extends DsDynamicInputModel {
@serializable() mandatoryField: string;
@serializable() relationFields: string[];
@serializable() scopeUUID: string;
- @serializable() value: any[];
+ @serializable() submissionScope: string;
+ @serializable() _value: any[];
@serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_RELATION;
constructor(config: DynamicGroupModelConfig, layout?: DynamicFormControlLayout) {
@@ -35,7 +39,32 @@ export class DynamicGroupModel extends DsDynamicInputModel {
this.mandatoryField = config.mandatoryField;
this.relationFields = config.relationFields;
this.scopeUUID = config.scopeUUID;
+ this.submissionScope = config.submissionScope;
const value = config.value || [];
- this.valueUpdates.next(value)
+ this.valueUpdates.next(value);
+ }
+
+ get value() {
+ if (isEmpty(this._value)) {
+ // If items is empty, last element has been removed
+ // so emit an empty value that allows to dispatch
+ // a remove JSON PATCH operation
+ const emptyItem = Object.create({});
+ emptyItem[this.mandatoryField] = null;
+ this.relationFields
+ .forEach((field) => {
+ emptyItem[field] = null;
+ });
+ return [emptyItem];
+ }
+ return this._value
+ }
+
+ set value(value) {
+ this._value = value;
+ }
+
+ isEmpty() {
+ return (this.value.length === 1 && isNull(this.value[0][this.mandatoryField]));
}
}
diff --git a/src/app/shared/form/builder/form-builder.service.ts b/src/app/shared/form/builder/form-builder.service.ts
index 2b0cddaaa4..b33018ea0c 100644
--- a/src/app/shared/form/builder/form-builder.service.ts
+++ b/src/app/shared/form/builder/form-builder.service.ts
@@ -162,12 +162,13 @@ export class FormBuilderService extends DynamicFormService {
Object.keys(groupValue)
.forEach((key) => {
const normValue = normalizeValue(controlModel, groupValue[key], groupIndex);
- if (iterateResult.hasOwnProperty(key)) {
- iterateResult[key].push(normValue);
- } else {
- iterateResult[key] = [normValue];
+ if (normValue.hasValue()) {
+ if (iterateResult.hasOwnProperty(key)) {
+ iterateResult[key].push(normValue);
+ } else {
+ iterateResult[key] = [normValue];
+ }
}
- // newGroupValue[key] = normalizeValue(controlModel, groupValue[key], groupIndex);
});
// controlArrayValue.push(newGroupValue);
})
diff --git a/src/app/shared/form/builder/parsers/group-field-parser.ts b/src/app/shared/form/builder/parsers/group-field-parser.ts
index cd897bb265..6c9ce6606a 100644
--- a/src/app/shared/form/builder/parsers/group-field-parser.ts
+++ b/src/app/shared/form/builder/parsers/group-field-parser.ts
@@ -13,7 +13,8 @@ export class GroupFieldParser extends FieldParser {
constructor(protected configData: FormFieldModel,
protected initFormValues,
protected readOnly: boolean,
- protected authorityUuid: string) {
+ protected submissionScope: string,
+ protected authorityUuid: string,) {
super(configData, initFormValues, readOnly);
}
@@ -21,6 +22,7 @@ export class GroupFieldParser extends FieldParser {
const modelConfiguration: DynamicGroupModelConfig = this.initModel();
modelConfiguration.scopeUUID = this.authorityUuid;
+ modelConfiguration.submissionScope = this.submissionScope;
if (this.configData && this.configData.rows && this.configData.rows.length > 0) {
modelConfiguration.formConfiguration = this.configData.rows;
modelConfiguration.relationFields = [];
diff --git a/src/app/shared/form/builder/parsers/row-parser.ts b/src/app/shared/form/builder/parsers/row-parser.ts
index 833ccfb6bd..3f76c1c5c1 100644
--- a/src/app/shared/form/builder/parsers/row-parser.ts
+++ b/src/app/shared/form/builder/parsers/row-parser.ts
@@ -90,7 +90,7 @@ export class RowParser {
break;
case 'group':
- fieldModel = new GroupFieldParser(fieldData, this.initFormValues, this.readOnly, this.authorityOptions.uuid).parse();
+ fieldModel = new GroupFieldParser(fieldData, this.initFormValues, this.readOnly, this.submissionScope, this.authorityOptions.uuid).parse();
break;
case 'twobox':
diff --git a/src/app/shared/form/form.component.html b/src/app/shared/form/form.component.html
index 0539a57000..5dfaa4bec1 100644
--- a/src/app/shared/form/form.component.html
+++ b/src/app/shared/form/form.component.html
@@ -6,6 +6,7 @@
[formGroup]="formGroup"
[formModel]="formModel"
[formLayout]="formLayout"
+ (change)="$event.stopPropagation();"
(dfBlur)="onBlur($event)"
(dfChange)="onChange($event)"
(dfFocus)="onFocus($event)">
diff --git a/src/app/shared/form/form.component.ts b/src/app/shared/form/form.component.ts
index 981aed083e..7a894ca3e5 100644
--- a/src/app/shared/form/form.component.ts
+++ b/src/app/shared/form/form.component.ts
@@ -8,6 +8,7 @@ import {
DynamicFormGroupModel, DynamicFormLayout,
} from '@ng-dynamic-forms/core';
import { Store } from '@ngrx/store';
+import { findIndex } from 'lodash';
import { AppState } from '../../app.reducer';
import {
@@ -37,6 +38,7 @@ import { isEmpty } from 'lodash';
})
export class FormComponent implements OnDestroy, OnInit {
+ private formErrors: FormError[] = [];
private formValid: boolean;
/**
@@ -44,6 +46,11 @@ export class FormComponent implements OnDestroy, OnInit {
*/
@Input() displaySubmit = true;
+ /**
+ * A boolean that indicate if to emit a form change event
+ */
+ @Input() emitChange = true;
+
/**
* The form unique ID
*/
@@ -154,7 +161,9 @@ export class FormComponent implements OnDestroy, OnInit {
.subscribe((errors: FormError[]) => {
const {formGroup, formModel} = this;
- errors.forEach((error: FormError) => {
+ errors
+ .filter((error: FormError) => findIndex(this.formErrors, {fieldId: error.fieldId}) === -1)
+ .forEach((error: FormError) => {
const {fieldId} = error;
let field: AbstractControl;
if (!!this.parentFormModel) {
@@ -166,9 +175,29 @@ export class FormComponent implements OnDestroy, OnInit {
if (field) {
const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel);
this.formService.addErrorToField(field, model, error.message);
+ // this.formService.validateAllFormFields(formGroup);
+ this.changeDetectorRef.detectChanges();
}
});
+ this.formErrors
+ .filter((error: FormError) => findIndex(errors, {fieldId: error.fieldId}) === -1)
+ .forEach((error: FormError) => {
+ const {fieldId} = error;
+ let field: AbstractControl;
+ if (!!this.parentFormModel) {
+ field = this.formBuilderService.getFormControlById(fieldId, formGroup.parent as FormGroup, formModel);
+ } else {
+ field = this.formBuilderService.getFormControlById(fieldId, formGroup, formModel);
+ }
+
+ if (field) {
+ const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel);
+ this.formService.removeErrorFromField(field, model, error.message);
+ }
+ });
+
+ this.formErrors = errors;
this.changeDetectorRef.detectChanges();
})
);
@@ -217,10 +246,11 @@ export class FormComponent implements OnDestroy, OnInit {
this.store.dispatch(action);
this.formGroup.markAsPristine();
- this.change.emit(event);
- const control: FormControl = event.control;
+ if (this.emitChange) {
+ this.change.emit(event);
+ }
- // control.setErrors(null);
+ const control: FormControl = event.control;
if (control.valid) {
this.store.dispatch(new FormRemoveErrorAction(this.formId, event.model.id));
}
diff --git a/src/app/shared/form/form.service.ts b/src/app/shared/form/form.service.ts
index f890d90012..f13bae1532 100644
--- a/src/app/shared/form/form.service.ts
+++ b/src/app/shared/form/form.service.ts
@@ -7,9 +7,9 @@ import { AppState } from '../../app.reducer';
import { formObjectFromIdSelector } from './selectors';
import { FormBuilderService } from './builder/form-builder.service';
import { DynamicFormControlModel, DynamicFormGroupModel } from '@ng-dynamic-forms/core';
-import { isNotEmpty, isNotUndefined } from '../empty.util';
+import { isEmpty, isNotEmpty, isNotUndefined } from '../empty.util';
import { find, uniqueId } from 'lodash';
-import { FormChangeAction } from './form.actions';
+import { FormChangeAction, FormRemoveErrorAction } from './form.actions';
@Injectable()
export class FormService {
@@ -75,39 +75,46 @@ export class FormService {
}
public addErrorToField(field: AbstractControl, model: DynamicFormControlModel, message: string) {
- const errorFound = !!(find(field.errors, (err) => err === message));
- // search for the same error in the formControl.errors property
- if (!errorFound) {
- const errorKey = uniqueId('error-'); // create a single key for the error
- const error = {}; // create the error object
+ const error = {}; // create the error object
- error[errorKey] = message; // assign message
-
- // if form control model has not errorMessages object, create it
- if (!model.errorMessages) {
- model.errorMessages = {};
- }
-
- // put the error in the form control model
- model.errorMessages[errorKey] = message;
-
- // Use correct error messages from the model
- const lastArray = message.split('.');
- if (lastArray && lastArray.length > 0) {
- const last = lastArray[lastArray.length - 1];
- const modelMsg = model.errorMessages[last];
- if (modelMsg && modelMsg.length > 0) {
- model.errorMessages[errorKey] = modelMsg;
- }
- }
-
- // add the error in the form control
- field.setErrors(error);
-
- // formGroup.markAsDirty();
- field.markAsTouched();
+ // if form control model has not errorMessages object, create it
+ if (!model.errorMessages) {
+ model.errorMessages = {};
}
+
+ // Use correct error messages from the model
+ const lastArray = message.split('.');
+ if (lastArray && lastArray.length > 0) {
+ // check if error code is already present in the set of model's validators
+ const last = lastArray[lastArray.length - 1];
+ const modelMsg = model.errorMessages[last];
+ if (isEmpty(modelMsg)) {
+ const errorKey = uniqueId('error-'); // create a single key for the error
+ error[errorKey] = true;
+ // put the error message in the form control model
+ model.errorMessages[errorKey] = message;
+ } else {
+ error[last] = modelMsg;
+ }
+ }
+
+ // add the error in the form control
+ field.setErrors(error);
+ field.markAsTouched();
+ }
+
+ public removeErrorFromField(field: AbstractControl, model: DynamicFormControlModel, message) {
+ const error = {};
+
+ // Use correct error messages from the model
+ const lastArray = message.split('.');
+ if (lastArray && lastArray.length > 0) {
+ const last = lastArray[lastArray.length - 1];
+ error[last] = null;
+ }
+ field.setErrors(error);
+ field.markAsUntouched();
}
public resetForm(formGroup: FormGroup, groupModel: DynamicFormControlModel[], formId: string) {