Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/app/core/json-patch/json-patch-operations.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { getTestScheduler } from 'jasmine-marbles';
import { getTestScheduler, hot } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing';
import { of as observableOf } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Store } from '@ngrx/store';

import { getMockRequestService } from '../../shared/mocks/request.service.mock';
import { RequestService } from '../data/request.service';
import { SubmissionPatchRequest } from '../data/request.models';
Expand All @@ -21,6 +20,7 @@ import {
StartTransactionPatchOperationsAction
} from './json-patch-operations.actions';
import { RequestEntry } from '../data/request.reducer';
import { _deepClone } from 'fast-json-patch/lib/helpers';

class TestService extends JsonPatchOperationsService<SubmitDataResponseDefinitionObject, SubmissionPatchRequest> {
protected linkPath = '';
Expand Down Expand Up @@ -179,6 +179,32 @@ describe('JsonPatchOperationsService test suite', () => {
});
});

describe('hasPendingOperations', () => {

it('should return true when there are pending operations', () => {

const expected = hot('(x|)', { x: true });

const result = service.hasPendingOperations(testJsonPatchResourceType);
expect(result).toBeObservable(expected);

});

it('should return false when there are not pending operations', () => {

const mockStateNoOp = _deepClone(mockState);
mockStateNoOp['json/patch'][testJsonPatchResourceType].children = [];
store.select.and.returnValue(observableOf(mockStateNoOp['json/patch'][testJsonPatchResourceType]));

const expected = hot('(x|)', { x: false });

const result = service.hasPendingOperations(testJsonPatchResourceType);
expect(result).toBeObservable(expected);

});

});

describe('jsonPatchByResourceID', () => {

it('should call submitJsonPatchOperations method', () => {
Expand Down
12 changes: 12 additions & 0 deletions src/app/core/json-patch/json-patch-operations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ export abstract class JsonPatchOperationsService<ResponseDefinitionDomain, Patch
return this.submitJsonPatchOperations(href$, resourceType);
}

/**
* Select the jsonPatch operation related to the specified resource type.
* @param resourceType
*/
public hasPendingOperations(resourceType: string): Observable<boolean> {
return this.store.select(jsonPatchOperationsByResourceType(resourceType)).pipe(
map((val) => !isEmpty(val) && Object.values(val.children)
.filter((section) => !isEmpty((section as any).body)).length > 0),
distinctUntilChanged(),
);
}

/**
* Make a new JSON Patch request with all operations related to the specified resource id
*
Expand Down
88 changes: 86 additions & 2 deletions src/app/shared/form/builder/form-builder.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
DYNAMIC_FORM_CONTROL_TYPE_GROUP,
DYNAMIC_FORM_CONTROL_TYPE_INPUT,
DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP,
DynamicFormArrayModel,
DynamicFormArrayModel, DynamicFormControlEvent,
DynamicFormControlModel,
DynamicFormGroupModel,
DynamicFormService, DynamicFormValidationService,
Expand All @@ -16,7 +16,7 @@ import {
import { isObject, isString, mergeWith } from 'lodash';

import { hasValue, isEmpty, isNotEmpty, isNotNull, isNotUndefined, isNull } from '../../empty.util';
import { DynamicQualdropModel } from './ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model';
import {DynamicQualdropModel} from './ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model';
import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model';
import { DYNAMIC_FORM_CONTROL_TYPE_TAG } from './ds-dynamic-form-ui/models/tag/dynamic-tag.model';
import { RowParser } from './parsers/row-parser';
Expand All @@ -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 { isNgbDateStruct } from '../../date.util';
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()
export class FormBuilderService extends DynamicFormService {
Expand Down Expand Up @@ -54,6 +55,13 @@ export class FormBuilderService extends DynamicFormService {
break;
}

if (this.isConcatGroup(controlModel)) {
if (controlModel.id.match(new RegExp(findId + CONCAT_GROUP_SUFFIX + `_\\d+$`))) {
result = (controlModel as DynamicConcatModel).group[0];
break;
}
}

if (this.isGroup(controlModel)) {
findByIdFn(findId, (controlModel as DynamicFormGroupModel).group, findArrayIndex);
}
Expand Down Expand Up @@ -247,6 +255,10 @@ export class FormBuilderService extends DynamicFormService {
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 {
return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isRowGroup === true);
}
Expand Down Expand Up @@ -303,4 +315,76 @@ export class FormBuilderService extends DynamicFormService {
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): string[] => {
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;
}

}
29 changes: 26 additions & 3 deletions src/app/shared/form/form.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { type } from '../ngrx/type';
export const FormActionTypes = {
FORM_INIT: type('dspace/form/FORM_INIT'),
FORM_CHANGE: type('dspace/form/FORM_CHANGE'),
FORM_ADDITIONAL: type('dspace/form/FORM_ADDITIONAL'),
FORM_REMOVE: type('dspace/form/FORM_REMOVE'),
FORM_STATUS_CHANGE: type('dspace/form/FORM_STATUS_CHANGE'),
FORM_ADD_ERROR: type('dspace/form/FORM_ADD_ERROR'),
Expand All @@ -27,6 +28,7 @@ export class FormInitAction implements Action {
formId: string;
formData: any;
valid: boolean;
formAdditional: any;
};

/**
Expand All @@ -39,8 +41,8 @@ export class FormInitAction implements Action {
* @param valid
* the Form validation status
*/
constructor(formId: string, formData: any, valid: boolean) {
this.payload = {formId, formData, valid};
constructor(formId: string, formData: any, valid: boolean, formAdditional?: any) {
this.payload = {formId, formData, valid, formAdditional};
}
}

Expand All @@ -52,7 +54,7 @@ export class FormChangeAction implements Action {
};

/**
* Create a new FormInitAction
* Create a new FormChangeAction
*
* @param formId
* the Form's ID
Expand All @@ -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 {
type = FormActionTypes.FORM_REMOVE;
payload: {
Expand Down Expand Up @@ -147,6 +169,7 @@ export class FormClearErrorsAction implements Action {
*/
export type FormAction = FormInitAction
| FormChangeAction
| FormSetAdditionalAction
| FormRemoveAction
| FormStatusChangeAction
| FormAddError
Expand Down
2 changes: 1 addition & 1 deletion src/app/shared/form/form.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<button type="button" class="btn btn-secondary"
[disabled]="isItemReadOnly(context, index)"
(click)="insertItem($event, group.context, group.index)">
<i class="fas fa-plus" aria-hidden="true"></i>
<span aria-label="Add">{{'form.add' | translate}}</span>
</button>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/app/shared/form/form.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ function init() {
dc_identifier_issn: null
},
valid: false,
errors: []
errors: [],
additional: {}
}
};

Expand Down
1 change: 1 addition & 0 deletions src/app/shared/form/form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export class FormComponent implements OnDestroy, OnInit {
}

onFocus(event: DynamicFormControlEvent): void {
this.formService.setTouched(this.formId, this.formModel, event);
this.focus.emit(event);
}

Expand Down
Loading