[835] Auto-save in new Item Submission form breaks the form

Store additions:
1. Form AdditionalData: contains the list of the touched metadata
2. Submission metadata: contains the list of the metadata ids assignable for each section

We keep also track whether a section ha focused fields or not.
This commit is contained in:
Alessandro Martelli
2020-11-19 15:47:03 +01:00
parent 82b7b8aa6f
commit 8111bdd3ce
10 changed files with 299 additions and 28 deletions

View File

@@ -7,7 +7,7 @@ import {
DYNAMIC_FORM_CONTROL_TYPE_GROUP, DYNAMIC_FORM_CONTROL_TYPE_GROUP,
DYNAMIC_FORM_CONTROL_TYPE_INPUT, DYNAMIC_FORM_CONTROL_TYPE_INPUT,
DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP,
DynamicFormArrayModel, DynamicFormArrayModel, DynamicFormControlEvent,
DynamicFormControlModel, DynamicFormControlModel,
DynamicFormGroupModel, DynamicFormGroupModel,
DynamicFormService, DynamicFormValidationService, DynamicFormService, DynamicFormValidationService,
@@ -26,6 +26,7 @@ import { DsDynamicInputModel } from './ds-dynamic-form-ui/models/ds-dynamic-inpu
import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model'; import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model';
import { isNgbDateStruct } from '../../date.util'; import { isNgbDateStruct } from '../../date.util';
import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-ui/ds-dynamic-form-constants'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-ui/ds-dynamic-form-constants';
import { CONCAT_GROUP_SUFFIX, DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model';
@Injectable() @Injectable()
export class FormBuilderService extends DynamicFormService { export class FormBuilderService extends DynamicFormService {
@@ -54,6 +55,15 @@ export class FormBuilderService extends DynamicFormService {
break; break;
} }
if (this.isConcatGroup(controlModel)) {
const concatGroupId = controlModel.id.replace(CONCAT_GROUP_SUFFIX, '');
// if (concatGroupId === findId) {
if (concatGroupId.includes(findId)) {
result = (controlModel as DynamicConcatModel).group[0];
break;
}
}
if (this.isGroup(controlModel)) { if (this.isGroup(controlModel)) {
findByIdFn(findId, (controlModel as DynamicFormGroupModel).group, findArrayIndex); findByIdFn(findId, (controlModel as DynamicFormGroupModel).group, findArrayIndex);
} }
@@ -247,6 +257,10 @@ export class FormBuilderService extends DynamicFormService {
return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isCustomGroup === true); return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isCustomGroup === true);
} }
isConcatGroup(model: DynamicFormControlModel): boolean {
return this.isCustomGroup(model) && (model.id.indexOf(CONCAT_GROUP_SUFFIX) !== -1);
}
isRowGroup(model: DynamicFormControlModel): boolean { isRowGroup(model: DynamicFormControlModel): boolean {
return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isRowGroup === true); return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isRowGroup === true);
} }
@@ -303,4 +317,76 @@ export class FormBuilderService extends DynamicFormService {
return (tempModel.id !== tempModel.name) ? tempModel.name : tempModel.id; return (tempModel.id !== tempModel.name) ? tempModel.name : tempModel.id;
} }
/**
* Calculate the metadata list related to the event.
* @param event
*/
getMetadataIdsFromEvent(event: DynamicFormControlEvent): string[] {
let model = event.model;
while (model.parent) {
model = model.parent as any;
}
const iterateControlModels = (findGroupModel: DynamicFormControlModel[], controlModelIndex: number = 0): void => {
let iterateResult = Object.create({});
// Iterate over all group's controls
for (const controlModel of findGroupModel) {
if (this.isRowGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) {
iterateResult = mergeWith(iterateResult, iterateControlModels((controlModel as DynamicFormGroupModel).group));
continue;
}
if (this.isGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) {
iterateResult[controlModel.name] = iterateControlModels((controlModel as DynamicFormGroupModel).group);
continue;
}
if (this.isRowArrayGroup(controlModel)) {
for (const arrayItemModel of (controlModel as DynamicRowArrayModel).groups) {
iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group, arrayItemModel.index));
}
continue;
}
if (this.isArrayGroup(controlModel)) {
iterateResult[controlModel.name] = [];
for (const arrayItemModel of (controlModel as DynamicFormArrayModel).groups) {
iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group, arrayItemModel.index));
}
continue;
}
let controlId;
// Get the field's name
if (this.isQualdropGroup(controlModel)) {
// If is instance of DynamicQualdropModel take the qualdrop id as field's name
controlId = (controlModel as DynamicQualdropModel).qualdropId;
} else {
controlId = controlModel.name;
}
if (this.isRelationGroup(controlModel)) {
const values = (controlModel as DynamicRelationGroupModel).getGroupValue();
values.forEach((groupValue, groupIndex) => {
Object.keys(groupValue).forEach((key) => {
iterateResult[key] = true;
});
});
} else {
iterateResult[controlId] = true;
}
}
return iterateResult;
};
const result = iterateControlModels([model]);
return result;
}
} }

View File

@@ -13,6 +13,7 @@ import { type } from '../ngrx/type';
export const FormActionTypes = { export const FormActionTypes = {
FORM_INIT: type('dspace/form/FORM_INIT'), FORM_INIT: type('dspace/form/FORM_INIT'),
FORM_CHANGE: type('dspace/form/FORM_CHANGE'), FORM_CHANGE: type('dspace/form/FORM_CHANGE'),
FORM_ADDITIONAL: type('dspace/form/FORM_ADDITIONAL'),
FORM_REMOVE: type('dspace/form/FORM_REMOVE'), FORM_REMOVE: type('dspace/form/FORM_REMOVE'),
FORM_STATUS_CHANGE: type('dspace/form/FORM_STATUS_CHANGE'), FORM_STATUS_CHANGE: type('dspace/form/FORM_STATUS_CHANGE'),
FORM_ADD_ERROR: type('dspace/form/FORM_ADD_ERROR'), FORM_ADD_ERROR: type('dspace/form/FORM_ADD_ERROR'),
@@ -27,6 +28,7 @@ export class FormInitAction implements Action {
formId: string; formId: string;
formData: any; formData: any;
valid: boolean; valid: boolean;
formAdditional: any;
}; };
/** /**
@@ -39,8 +41,8 @@ export class FormInitAction implements Action {
* @param valid * @param valid
* the Form validation status * the Form validation status
*/ */
constructor(formId: string, formData: any, valid: boolean) { constructor(formId: string, formData: any, valid: boolean, formAdditional?: any) {
this.payload = {formId, formData, valid}; this.payload = {formId, formData, valid, formAdditional};
} }
} }
@@ -52,7 +54,7 @@ export class FormChangeAction implements Action {
}; };
/** /**
* Create a new FormInitAction * Create a new FormChangeAction
* *
* @param formId * @param formId
* the Form's ID * the Form's ID
@@ -64,6 +66,26 @@ export class FormChangeAction implements Action {
} }
} }
export class FormSetAdditionalAction implements Action {
type = FormActionTypes.FORM_ADDITIONAL;
payload: {
formId: string;
additionalData: any;
};
/**
* Create a new FormSetAdditionalAction
*
* @param formId
* the Form's ID
* @param additionalData
* the additionalData Object
*/
constructor(formId: string, additionalData: any) {
this.payload = {formId, additionalData};
}
}
export class FormRemoveAction implements Action { export class FormRemoveAction implements Action {
type = FormActionTypes.FORM_REMOVE; type = FormActionTypes.FORM_REMOVE;
payload: { payload: {
@@ -147,6 +169,7 @@ export class FormClearErrorsAction implements Action {
*/ */
export type FormAction = FormInitAction export type FormAction = FormInitAction
| FormChangeAction | FormChangeAction
| FormSetAdditionalAction
| FormRemoveAction | FormRemoveAction
| FormStatusChangeAction | FormStatusChangeAction
| FormAddError | FormAddError

View File

@@ -5,7 +5,7 @@ import {
FormChangeAction, FormClearErrorsAction, FormChangeAction, FormClearErrorsAction,
FormInitAction, FormInitAction,
FormRemoveAction, FormRemoveAction,
FormRemoveErrorAction, FormRemoveErrorAction, FormSetAdditionalAction,
FormStatusChangeAction FormStatusChangeAction
} from './form.actions'; } from './form.actions';
import { hasValue } from '../empty.util'; import { hasValue } from '../empty.util';
@@ -21,6 +21,7 @@ export interface FormEntry {
data: any; data: any;
valid: boolean; valid: boolean;
errors: FormError[]; errors: FormError[];
additional: any;
} }
export interface FormState { export interface FormState {
@@ -40,6 +41,10 @@ export function formReducer(state = initialState, action: FormAction): FormState
return changeDataForm(state, action as FormChangeAction); return changeDataForm(state, action as FormChangeAction);
} }
case FormActionTypes.FORM_ADDITIONAL: {
return additionalData(state, action as FormSetAdditionalAction);
}
case FormActionTypes.FORM_REMOVE: { case FormActionTypes.FORM_REMOVE: {
return removeForm(state, action as FormRemoveAction); return removeForm(state, action as FormRemoveAction);
} }
@@ -127,7 +132,8 @@ function initForm(state: FormState, action: FormInitAction): FormState {
const formState = { const formState = {
data: action.payload.formData, data: action.payload.formData,
valid: action.payload.valid, valid: action.payload.valid,
errors: [] errors: [],
additional: action.payload.formAdditional
}; };
if (!hasValue(state[action.payload.formId])) { if (!hasValue(state[action.payload.formId])) {
return Object.assign({}, state, { return Object.assign({}, state, {
@@ -212,3 +218,30 @@ function removeForm(state: FormState, action: FormRemoveAction): FormState {
return state; return state;
} }
} }
/**
* Compute the additional data state of the form. New touched fields are merged with the previous ones.
* @param state
* @param action
*/
function additionalData(state: FormState, action: FormSetAdditionalAction): FormState {
if (hasValue(state[action.payload.formId])) {
const newState = Object.assign({}, state);
const newAdditional = newState[action.payload.formId].additional ? {...newState[action.payload.formId].additional} : {};
const newTouchedValue = newAdditional.touched ? {...newAdditional.touched,
...action.payload.additionalData.touched} : { ...action.payload.additionalData.touched};
newAdditional.touched = newTouchedValue;
newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], {
additional: newAdditional
}
);
return newState;
} else {
return state;
}
}

View File

@@ -7,13 +7,13 @@ import { select, Store } from '@ngrx/store';
import { AppState } from '../../app.reducer'; import { AppState } from '../../app.reducer';
import { formObjectFromIdSelector } from './selectors'; import { formObjectFromIdSelector } from './selectors';
import { FormBuilderService } from './builder/form-builder.service'; import { FormBuilderService } from './builder/form-builder.service';
import { DynamicFormControlModel } from '@ng-dynamic-forms/core'; import {DynamicFormControlEvent, DynamicFormControlModel} from '@ng-dynamic-forms/core';
import { isEmpty, isNotUndefined } from '../empty.util'; import { isEmpty, isNotUndefined } from '../empty.util';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import { import {
FormChangeAction, FormChangeAction,
FormInitAction, FormInitAction,
FormRemoveAction, FormRemoveErrorAction, FormRemoveAction, FormRemoveErrorAction, FormSetAdditionalAction,
FormStatusChangeAction FormStatusChangeAction
} from './form.actions'; } from './form.actions';
import { FormEntry } from './form.reducer'; import { FormEntry } from './form.reducer';
@@ -51,6 +51,18 @@ export class FormService {
); );
} }
/**
* Method to retrieve form's additional data from state
*/
public getFormAdditionalData(formId: string): Observable<any> {
return this.store.pipe(
select(formObjectFromIdSelector(formId)),
filter((state) => isNotUndefined(state)),
map((state) => state.additional),
distinctUntilChanged()
);
}
/** /**
* Method to retrieve form's errors from state * Method to retrieve form's errors from state
*/ */
@@ -149,8 +161,8 @@ export class FormService {
return (environment.form.validatorMap.hasOwnProperty(validator)) ? environment.form.validatorMap[validator] : validator; return (environment.form.validatorMap.hasOwnProperty(validator)) ? environment.form.validatorMap[validator] : validator;
} }
public initForm(formId: string, model: DynamicFormControlModel[], valid: boolean) { public initForm(formId: string, model: DynamicFormControlModel[], valid: boolean, additional?: any) {
this.store.dispatch(new FormInitAction(formId, this.formBuilderService.getValueFromModel(model), valid)); this.store.dispatch(new FormInitAction(formId, this.formBuilderService.getValueFromModel(model), valid, additional));
} }
public setStatusChanged(formId: string, valid: boolean) { public setStatusChanged(formId: string, valid: boolean) {
@@ -169,6 +181,11 @@ export class FormService {
this.store.dispatch(new FormChangeAction(formId, this.formBuilderService.getValueFromModel(model))); this.store.dispatch(new FormChangeAction(formId, this.formBuilderService.getValueFromModel(model)));
} }
public setTouched(formId: string, model: DynamicFormControlModel[], event: DynamicFormControlEvent) {
const ids = this.formBuilderService.getMetadataIdsFromEvent(event);
this.store.dispatch(new FormSetAdditionalAction(formId, { touched: ids}));
}
public removeError(formId: string, eventModelId: string, fieldIndex: number) { public removeError(formId: string, eventModelId: string, fieldIndex: number) {
this.store.dispatch(new FormRemoveErrorAction(formId, eventModelId, fieldIndex)); this.store.dispatch(new FormRemoveErrorAction(formId, eventModelId, fieldIndex));
} }

View File

@@ -206,6 +206,7 @@ export class UpdateSectionDataAction implements Action {
sectionId: string; sectionId: string;
data: WorkspaceitemSectionDataType; data: WorkspaceitemSectionDataType;
errors: SubmissionSectionError[]; errors: SubmissionSectionError[];
metadata: string[];
}; };
/** /**
@@ -219,12 +220,15 @@ export class UpdateSectionDataAction implements Action {
* the section's data * the section's data
* @param errors * @param errors
* the section's errors * the section's errors
* @param metadata
* the section's metadata
*/ */
constructor(submissionId: string, constructor(submissionId: string,
sectionId: string, sectionId: string,
data: WorkspaceitemSectionDataType, data: WorkspaceitemSectionDataType,
errors: SubmissionSectionError[]) { errors: SubmissionSectionError[],
this.payload = { submissionId, sectionId, data, errors }; metadata?: string[]) {
this.payload = { submissionId, sectionId, data, errors, metadata };
} }
} }

View File

@@ -292,7 +292,7 @@ export class SubmissionObjectEffects {
return item$.pipe( return item$.pipe(
map((item: Item) => item.metadata), map((item: Item) => item.metadata),
filter((metadata) => !isEqual(action.payload.data, metadata)), filter((metadata) => !isEqual(action.payload.data, metadata)),
map((metadata: any) => new UpdateSectionDataAction(action.payload.submissionId, action.payload.sectionId, metadata, action.payload.errors)) map((metadata: any) => new UpdateSectionDataAction(action.payload.submissionId, action.payload.sectionId, metadata, action.payload.errors, action.payload.metadata))
); );
} else { } else {
return observableOf(new UpdateSectionDataSuccessAction()); return observableOf(new UpdateSectionDataSuccessAction());

View File

@@ -85,6 +85,11 @@ export interface SubmissionSectionObject {
*/ */
enabled: boolean; enabled: boolean;
/**
* The list of the metadata ids of the section.
*/
metadata: string[];
/** /**
* The section data object * The section data object
*/ */
@@ -660,7 +665,8 @@ function updateSectionData(state: SubmissionObjectState, action: UpdateSectionDa
[ action.payload.sectionId ]: Object.assign({}, state[ action.payload.submissionId ].sections [ action.payload.sectionId ], { [ action.payload.sectionId ]: Object.assign({}, state[ action.payload.submissionId ].sections [ action.payload.sectionId ], {
enabled: true, enabled: true,
data: action.payload.data, data: action.payload.data,
errors: action.payload.errors errors: action.payload.errors,
metadata: reduceSectionMetadata(action.payload.metadata, state[ action.payload.submissionId ].sections [ action.payload.sectionId ].metadata)
}) })
}) })
}) })
@@ -670,6 +676,22 @@ function updateSectionData(state: SubmissionObjectState, action: UpdateSectionDa
} }
} }
/**
* Updates the state of the section metadata only when a new value is provided.
* Keep the existent otherwise.
* @param newMetadata
* @param oldMetadata
*/
function reduceSectionMetadata(newMetadata: string[], oldMetadata: string[]) {
if (newMetadata) {
return newMetadata;
}
if (oldMetadata) {
return [...oldMetadata];
}
return undefined;
}
/** /**
* Set a section state. * Set a section state.
* *

View File

@@ -2,7 +2,9 @@
<ds-form *ngIf="!isLoading && formModel" #formRef="formComponent" <ds-form *ngIf="!isLoading && formModel" #formRef="formComponent"
[formId]="formId" [formId]="formId"
[formModel]="formModel" [formModel]="formModel"
[formAdditional]="formAdditionalData"
[displaySubmit]="false" [displaySubmit]="false"
(dfBlur)="onBlur($event)"
(dfChange)="onChange($event)" (dfChange)="onChange($event)"
(dfFocus)="onFocus($event)" (dfFocus)="onFocus($event)"
(remove)="onRemove($event)" (remove)="onRemove($event)"

View File

@@ -20,7 +20,7 @@ import { FormComponent } from '../../../shared/form/form.component';
import { FormService } from '../../../shared/form/form.service'; import { FormService } from '../../../shared/form/form.service';
import { SectionModelComponent } from '../models/section.model'; import { SectionModelComponent } from '../models/section.model';
import { SubmissionFormsConfigService } from '../../../core/config/submission-forms-config.service'; import { SubmissionFormsConfigService } from '../../../core/config/submission-forms-config.service';
import { hasNoValue, hasValue, isNotEmpty, isUndefined } from '../../../shared/empty.util'; import { hasNoValue, hasValue, isEmpty, isNotEmpty, isUndefined } from '../../../shared/empty.util';
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model'; import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model';
import { import {
@@ -101,6 +101,18 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
*/ */
protected formData: any = Object.create({}); protected formData: any = Object.create({});
/**
* Store the current form additional data
* @protected
*/
protected formAdditionalData: any = Object.create({});
/**
* Store the
* @protected
*/
protected sectionMetadata: string[];
/** /**
* The [JsonPatchOperationPathCombiner] object * The [JsonPatchOperationPathCombiner] object
* @type {JsonPatchOperationPathCombiner} * @type {JsonPatchOperationPathCombiner}
@@ -125,6 +137,12 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
*/ */
@ViewChild('formRef', {static: false}) private formRef: FormComponent; @ViewChild('formRef', {static: false}) private formRef: FormComponent;
/**
* Keep track whether the section is focused or not.
* @protected
*/
protected isFocused = false;
/** /**
* Initialize instance variables * Initialize instance variables
* *
@@ -230,16 +248,24 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
* the section data retrieved from the server * the section data retrieved from the server
*/ */
hasMetadataEnrichment(sectionData: WorkspaceitemSectionFormObject): boolean { hasMetadataEnrichment(sectionData: WorkspaceitemSectionFormObject): boolean {
const sectionDataToCheck = {};
Object.keys(sectionData).forEach((key) => {
if (this.sectionMetadata.includes(key)) {
sectionDataToCheck[key] = sectionData[key];
}
})
const diffResult = []; const diffResult = [];
// compare current form data state with section data retrieved from store // compare current form data state with section data retrieved from store
const diffObj = difference(sectionData, this.formData); const diffObj = difference(sectionDataToCheck, this.formData);
// iterate over differences to check whether they are actually different // iterate over differences to check whether they are actually different
Object.keys(diffObj) Object.keys(diffObj)
.forEach((key) => { .forEach((key) => {
diffObj[key].forEach((value) => { diffObj[key].forEach((value) => {
if (value.hasOwnProperty('value')) { if (value.hasOwnProperty('value') && !isEmpty(value.value)) {
diffResult.push(value); diffResult.push(value);
} }
}); });
@@ -262,6 +288,9 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
sectionData, sectionData,
this.submissionService.getSubmissionScope() this.submissionService.getSubmissionScope()
); );
this.formBuilderService.enrichWithAdditionalData(this.formModel, this.formAdditionalData);
this.sectionMetadata = this.sectionService.computeSectionConfiguredMetadata(this.formConfig);
} catch (e) { } catch (e) {
const msg: string = this.translate.instant('error.submission.sections.init-form-error') + e.toString(); const msg: string = this.translate.instant('error.submission.sections.init-form-error') + e.toString();
const sectionError: SubmissionSectionError = { const sectionError: SubmissionSectionError = {
@@ -283,8 +312,9 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
*/ */
updateForm(sectionData: WorkspaceitemSectionFormObject, errors: SubmissionSectionError[]): void { updateForm(sectionData: WorkspaceitemSectionFormObject, errors: SubmissionSectionError[]): void {
if (hasValue(sectionData) && !isEqual(sectionData, this.sectionData.data)) { if (isNotEmpty(sectionData) && !isEqual(sectionData, this.sectionData.data)) {
this.sectionData.data = sectionData; this.sectionData.data = sectionData;
if (this.hasMetadataEnrichment(sectionData)) {
this.isUpdating = true; this.isUpdating = true;
this.formModel = null; this.formModel = null;
this.cdr.detectChanges(); this.cdr.detectChanges();
@@ -295,6 +325,9 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
} else if (isNotEmpty(errors) || isNotEmpty(this.sectionData.errors)) { } else if (isNotEmpty(errors) || isNotEmpty(this.sectionData.errors)) {
this.checksForErrors(errors); this.checksForErrors(errors);
} }
} else if (isNotEmpty(errors) || isNotEmpty(this.sectionData.errors)) {
this.checksForErrors(errors);
}
} }
@@ -308,6 +341,9 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
this.formService.isFormInitialized(this.formId).pipe( this.formService.isFormInitialized(this.formId).pipe(
find((status: boolean) => status === true && !this.isUpdating)) find((status: boolean) => status === true && !this.isUpdating))
.subscribe(() => { .subscribe(() => {
// TODO: filter these errors to only those that had been touched
this.sectionService.checkSectionErrors(this.submissionId, this.sectionData.id, this.formId, errors, this.sectionData.errors); this.sectionService.checkSectionErrors(this.submissionId, this.sectionData.id, this.formId, errors, this.sectionData.errors);
this.sectionData.errors = errors; this.sectionData.errors = errors;
this.cdr.detectChanges(); this.cdr.detectChanges();
@@ -328,6 +364,12 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
this.formData = formData; this.formData = formData;
}), }),
this.formService.getFormAdditionalData(this.formId).pipe(
distinctUntilChanged())
.subscribe((formAdditional) => {
this.formAdditionalData = formAdditional;
}),
/** /**
* Subscribe to section state * Subscribe to section state
*/ */
@@ -375,6 +417,7 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
* the [[DynamicFormControlEvent]] emitted * the [[DynamicFormControlEvent]] emitted
*/ */
onFocus(event: DynamicFormControlEvent): void { onFocus(event: DynamicFormControlEvent): void {
this.isFocused = true;
const value = this.formOperationsService.getFieldValueFromChangeEvent(event); const value = this.formOperationsService.getFieldValueFromChangeEvent(event);
const path = this.formBuilderService.getPath(event.model); const path = this.formBuilderService.getPath(event.model);
if (this.formBuilderService.hasMappedGroupValue(event.model)) { if (this.formBuilderService.hasMappedGroupValue(event.model)) {
@@ -386,6 +429,17 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
} }
} }
/**
* Method called when a form dfBlur event is fired.
*
* @param event
* the [[DynamicFormControlEvent]] emitted
*/
onBlur(event: DynamicFormControlEvent): void {
this.isFocused = false;
}
/** /**
* Method called when a form remove event is fired. * Method called when a form remove event is fired.
* Dispatch form operations based on changes. * Dispatch form operations based on changes.

View File

@@ -8,7 +8,7 @@ import { ScrollToConfigOptions, ScrollToService } from '@nicky-lenaers/ngx-scrol
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { SubmissionState } from '../submission.reducers'; import { SubmissionState } from '../submission.reducers';
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { hasValue, isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util';
import { import {
DisableSectionAction, DisableSectionAction,
EnableSectionAction, EnableSectionAction,
@@ -36,6 +36,8 @@ import { SubmissionService } from '../submission.service';
import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model'; import { WorkspaceitemSectionDataType } from '../../core/submission/models/workspaceitem-sections.model';
import { SectionsType } from './sections-type'; import { SectionsType } from './sections-type';
import { normalizeSectionData } from '../../core/submission/submission-response-parsing.service'; import { normalizeSectionData } from '../../core/submission/submission-response-parsing.service';
import { SubmissionFormsModel } from '../../core/config/models/config-submission-forms.model';
import { parseReviver } from '@ng-dynamic-forms/core';
/** /**
* A service that provides methods used in submission process. * A service that provides methods used in submission process.
@@ -335,8 +337,10 @@ export class SectionsService {
* The section data * The section data
* @param errors * @param errors
* The list of section errors * The list of section errors
* @param metadata
* The section metadata
*/ */
public updateSectionData(submissionId: string, sectionId: string, data: WorkspaceitemSectionDataType, errors: SubmissionSectionError[] = []) { public updateSectionData(submissionId: string, sectionId: string, data: WorkspaceitemSectionDataType, errors: SubmissionSectionError[] = [], metadata?: string[]) {
if (isNotEmpty(data)) { if (isNotEmpty(data)) {
const isAvailable$ = this.isSectionAvailable(submissionId, sectionId); const isAvailable$ = this.isSectionAvailable(submissionId, sectionId);
const isEnabled$ = this.isSectionEnabled(submissionId, sectionId); const isEnabled$ = this.isSectionEnabled(submissionId, sectionId);
@@ -345,7 +349,7 @@ export class SectionsService {
take(1), take(1),
filter(([available, enabled]: [boolean, boolean]) => available)) filter(([available, enabled]: [boolean, boolean]) => available))
.subscribe(([available, enabled]: [boolean, boolean]) => { .subscribe(([available, enabled]: [boolean, boolean]) => {
this.store.dispatch(new UpdateSectionDataAction(submissionId, sectionId, data, errors)); this.store.dispatch(new UpdateSectionDataAction(submissionId, sectionId, data, errors, metadata));
}); });
} }
} }
@@ -377,4 +381,30 @@ export class SectionsService {
public setSectionStatus(submissionId: string, sectionId: string, status: boolean) { public setSectionStatus(submissionId: string, sectionId: string, status: boolean) {
this.store.dispatch(new SectionStatusChangeAction(submissionId, sectionId, status)); this.store.dispatch(new SectionStatusChangeAction(submissionId, sectionId, status));
} }
/**
* Compute the list of selectable metadata for the section configuration.
* @param formConfig
*/
public computeSectionConfiguredMetadata(formConfig: string | SubmissionFormsModel): string[] {
const metadata = [];
const rawData = typeof formConfig === 'string' ? JSON.parse(formConfig, parseReviver) : formConfig;
if (rawData.rows && !isEmpty(rawData.rows)) {
rawData.rows.forEach((currentRow) => {
if (currentRow.fields && !isEmpty(currentRow.fields)) {
currentRow.fields.forEach((field) => {
if (field.selectableMetadata && !isEmpty(field.selectableMetadata)) {
field.selectableMetadata.forEach((selectableMetadata) => {
if (!metadata.includes(selectableMetadata.metadata)) {
metadata.push(selectableMetadata.metadata);
}
})
}
})
}
});
}
return metadata;
}
} }