mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-09 02:54:13 +00:00
[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:
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
|
@@ -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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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());
|
||||||
|
@@ -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.
|
||||||
*
|
*
|
||||||
|
@@ -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)"
|
||||||
|
@@ -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.
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user