diff --git a/src/app/core/json-patch/builder/json-patch-operations-builder.ts b/src/app/core/json-patch/builder/json-patch-operations-builder.ts index 9fe35b4dbe..1d3e15b543 100644 --- a/src/app/core/json-patch/builder/json-patch-operations-builder.ts +++ b/src/app/core/json-patch/builder/json-patch-operations-builder.ts @@ -7,7 +7,7 @@ import { } from '../json-patch-operations.actions'; import { JsonPatchOperationPathObject } from './json-patch-operation-path-combiner'; import { Injectable } from '@angular/core'; -import { isEmpty, isNotEmpty } from '../../../shared/empty.util'; +import { hasNoValue, isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { dateToISOFormat } from '../../../shared/date.util'; import { AuthorityValue } from '../../integration/models/authority.value'; import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; @@ -53,12 +53,16 @@ export class JsonPatchOperationsBuilder { * a boolean representing if the value to be added is a plain text value */ replace(path: JsonPatchOperationPathObject, value, plain = false) { - this.store.dispatch( - new NewPatchReplaceOperationAction( - path.rootElement, - path.subRootElement, - path.path, - this.prepareValue(value, plain, false))); + if (hasNoValue(value) || (typeof value === 'object' && hasNoValue(value.value))) { + this.remove(path); + } else { + this.store.dispatch( + new NewPatchReplaceOperationAction( + path.rootElement, + path.subRootElement, + path.path, + this.prepareValue(value, plain, false))); + } } move(path: JsonPatchOperationPathObject, prevPath: string) { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 0f4727faf8..82621de8b1 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -74,7 +74,7 @@ import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/relation-group/dynamic-relation-group.model'; import { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component'; import { map, startWith, switchMap, find, take, tap, filter } from 'rxjs/operators'; -import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; +import { combineLatest, combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; import { SearchResult } from '../../../search/search-result.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; @@ -100,6 +100,7 @@ import { FormService } from '../../form.service'; import { deepClone } from 'fast-json-patch'; import { SelectableListState } from '../../../object-list/selectable-list/selectable-list.reducer'; import { SubmissionService } from '../../../../submission/submission.service'; +import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type | null { switch (model.type) { @@ -187,6 +188,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo isRelationship: boolean; modalRef: NgbModalRef; item: Item; + item$: Observable; collection: Collection; listId: string; searchConfig: string; @@ -245,13 +247,13 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo .pipe( getAllSucceededRemoteData(), getRemoteDataPayload()); - this.relationshipValue$ = relationship$.pipe( - switchMap((relationship: Relationship) => + this.relationshipValue$ = combineLatest(this.item$.pipe(take(1)), relationship$).pipe( + switchMap(([item, relationship]: [Item, Relationship]) => relationship.leftItem.pipe( getSucceededRemoteData(), getRemoteDataPayload(), map((leftItem: Item) => { - return new ReorderableRelationship(relationship, leftItem.uuid !== this.item.uuid, this.relationshipService) + return new ReorderableRelationship(relationship, leftItem.uuid !== item.uuid, this.relationshipService, this.store, this.model.submissionId) }), ) ), @@ -264,10 +266,11 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo this.setItem(); const subscription = this.selectableListService.getSelectableList(this.listId).pipe( find((list: SelectableListState) => hasNoValue(list)), - switchMap(() => { - return this.relationService.getRelatedItemsByLabel(this.item, this.model.relationshipConfig.relationshipType).pipe( + switchMap(() => this.item$.pipe(take(1))), + switchMap((item) => { + return this.relationService.getRelatedItemsByLabel(item, this.model.relationshipConfig.relationshipType).pipe( getSucceededRemoteData(), - map((items: RemoteData>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))), + map((items: RemoteData>) => items.payload.page.map((i) => Object.assign(new ItemSearchResult(), { indexableObject: i }))), ) }) ).subscribe((relatedItems: Array>) => this.selectableListService.select(this.listId, relatedItems)); @@ -328,7 +331,10 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo modalComp.query = this.model.value ? this.model.value.value : ''; if (hasValue(this.model.value)) { - this.model.valueUpdates.next(''); + // this.model.reset(); + // this.model.value = undefined; + // this.model.valueUpdates(undefined); + this.model.value.value = undefined; this.change.emit(); } this.submissionService.dispatchSave(this.model.submissionId); @@ -358,10 +364,10 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo getRemoteDataPayload() ); - const item$ = submissionObject$.pipe(switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()))); + this.item$ = submissionObject$.pipe(switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()))); const collection$ = submissionObject$.pipe(switchMap((submissionObject: SubmissionObject) => (submissionObject.collection as Observable>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()))); - this.subs.push(item$.subscribe((item) => this.item = item)); + this.subs.push(this.item$.subscribe((item) => this.item = item)); this.subs.push(collection$.subscribe((collection) => this.collection = collection)); } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts index 5b5c30e22c..81381e6794 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts @@ -23,7 +23,7 @@ import { SelectableListService } from '../../../../object-list/selectable-list/s import { FormFieldMetadataValueObject } from '../../models/form-field-metadata-value.model'; import { RelationshipOptions } from '../../models/relationship-options.model'; import { DynamicConcatModel } from '../models/ds-dynamic-concat.model'; -import { RemoveRelationshipAction } from '../relation-lookup-modal/relationship.actions'; +import { RemoveRelationshipAction, UpdateRelationshipAction } from '../relation-lookup-modal/relationship.actions'; import { SubmissionObject } from '../../../../../core/submission/models/submission-object.model'; // tslint:disable:max-classes-per-file @@ -85,7 +85,14 @@ export class ReorderableFormFieldMetadataValue extends Reorderable { */ export class ReorderableRelationship extends Reorderable { - constructor(public relationship: Relationship, public useLeftItem: boolean, protected relationshipService: RelationshipService, oldIndex?: number, newIndex?: number) { + constructor( + public relationship: Relationship, + public useLeftItem: boolean, + protected relationshipService: RelationshipService, + protected store: Store, + protected submissionID: string, + oldIndex?: number, + newIndex?: number) { super(oldIndex, newIndex); this.relationship = relationship; this.useLeftItem = useLeftItem; @@ -104,6 +111,7 @@ export class ReorderableRelationship extends Reorderable { } update(): Observable> { + this.store.dispatch(new UpdateRelationshipAction(this.relationship, this.submissionID)) const updatedRelationship$ = this.relationshipService.updatePlace(this).pipe( getSucceededRemoteData(), ); @@ -147,23 +155,25 @@ export class ExistingMetadataListElementComponent implements OnChanges, OnDestro } ngOnChanges() { - const item$ = this.reoRel.useLeftItem ? - this.reoRel.relationship.leftItem : this.reoRel.relationship.rightItem; - this.subs.push(item$.pipe( - getAllSucceededRemoteData(), - getRemoteDataPayload(), - filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid)) - ).subscribe((item: Item) => { - this.relatedItem = item; - const relationMD: MetadataValue = this.submissionItem.firstMetadata(this.relationshipOptions.metadataField, { value: this.relatedItem.uuid }); - if (hasValue(relationMD)) { - const metadataRepresentationMD: MetadataValue = this.submissionItem.firstMetadata(this.metadataFields, { authority: relationMD.authority }); - this.metadataRepresentation = Object.assign( - new ItemMetadataRepresentation(metadataRepresentationMD), - this.relatedItem - ) - } - })); + if (hasValue(this.reoRel)) { + const item$ = this.reoRel.useLeftItem ? + this.reoRel.relationship.leftItem : this.reoRel.relationship.rightItem; + this.subs.push(item$.pipe( + getAllSucceededRemoteData(), + getRemoteDataPayload(), + filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid)) + ).subscribe((item: Item) => { + this.relatedItem = item; + const relationMD: MetadataValue = this.submissionItem.firstMetadata(this.relationshipOptions.metadataField, { value: this.relatedItem.uuid }); + if (hasValue(relationMD)) { + const metadataRepresentationMD: MetadataValue = this.submissionItem.firstMetadata(this.metadataFields, { authority: relationMD.authority }); + this.metadataRepresentation = Object.assign( + new ItemMetadataRepresentation(metadataRepresentationMD), + this.relatedItem + ) + } + })); + } } /** @@ -184,4 +194,5 @@ export class ExistingMetadataListElementComponent implements OnChanges, OnDestro } } + // tslint:enable:max-classes-per-file diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts index 30c5bb828b..2864688bb5 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts @@ -40,6 +40,7 @@ import { SubmissionState } from '../../../../../../submission/submission.reducer import { ObjectCacheService } from '../../../../../../core/cache/object-cache.service'; import { RequestService } from '../../../../../../core/data/request.service'; import { SubmissionService } from '../../../../../../submission/submission.service'; +import { AppState } from '../../../../../../app.reducer'; @Component({ selector: 'ds-dynamic-form-array', @@ -72,6 +73,7 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent imple protected zone: NgZone, protected formService: DynamicFormService, private submissionService: SubmissionService, + private store: Store, ) { super(layoutService, validationService); } @@ -87,9 +89,11 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent imple getRemoteDataPayload() ) ) - ).subscribe((item) => this.submissionItem = item); + ).subscribe((item) => { + this.submissionItem = item; + this.updateReorderables(); + }); - this.updateReorderables(); } private updateReorderables(): void { @@ -123,6 +127,8 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent imple relationship, leftItem.uuid !== this.submissionItem.uuid, this.relationshipService, + this.store, + this.model.submissionId, index, index ); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts index b427b5a0a0..be223f3eac 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts @@ -12,7 +12,7 @@ import { RelationshipOptions } from '../../models/relationship-options.model'; import { SearchResult } from '../../../../search/search-result.model'; import { Item } from '../../../../../core/shared/item.model'; import { getAllSucceededRemoteData, getRemoteDataPayload } from '../../../../../core/shared/operators'; -import { AddRelationshipAction, RemoveRelationshipAction, UpdateRelationshipAction } from './relationship.actions'; +import { AddRelationshipAction, RemoveRelationshipAction, UpdateRelationshipNameVariantAction } from './relationship.actions'; import { RelationshipService } from '../../../../../core/data/relationship.service'; import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service'; import { Store } from '@ngrx/store'; @@ -178,7 +178,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy const nameVariant$ = this.relationshipService.getNameVariant(this.listId, sri.indexableObject.uuid); this.subMap[sri.indexableObject.uuid] = nameVariant$.pipe( skip(1), - ).subscribe((nameVariant: string) => this.store.dispatch(new UpdateRelationshipAction(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, this.submissionId, nameVariant))) + ).subscribe((nameVariant: string) => this.store.dispatch(new UpdateRelationshipNameVariantAction(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, this.submissionId, nameVariant))) } /** diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts index 456c7c3f8f..6b494958c5 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.actions.ts @@ -4,10 +4,12 @@ import { type } from '../../../../ngrx/type'; import { Action } from '@ngrx/store'; import { Item } from '../../../../../core/shared/item.model'; +import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; export const RelationshipActionTypes = { ADD_RELATIONSHIP: type('dspace/relationship/ADD_RELATIONSHIP'), REMOVE_RELATIONSHIP: type('dspace/relationship/REMOVE_RELATIONSHIP'), + UPDATE_NAME_VARIANT: type('dspace/relationship/UPDATE_NAME_VARIANT'), UPDATE_RELATIONSHIP: type('dspace/relationship/UPDATE_RELATIONSHIP'), }; @@ -46,8 +48,8 @@ export class AddRelationshipAction implements Action { } } -export class UpdateRelationshipAction implements Action { - type = RelationshipActionTypes.UPDATE_RELATIONSHIP; +export class UpdateRelationshipNameVariantAction implements Action { + type = RelationshipActionTypes.UPDATE_NAME_VARIANT; payload: { item1: Item; @@ -58,7 +60,7 @@ export class UpdateRelationshipAction implements Action { }; /** - * Create a new UpdateRelationshipAction + * Create a new UpdateRelationshipNameVariantAction * * @param item1 The first item in the relationship * @param item2 The second item in the relationship @@ -77,6 +79,28 @@ export class UpdateRelationshipAction implements Action { } } +export class UpdateRelationshipAction implements Action { + type = RelationshipActionTypes.UPDATE_RELATIONSHIP; + + payload: { + relationship: Relationship; + submissionId: string; + }; + + /** + * Create a new UpdateRelationshipAction + * + * @param relationship The relationship + * @param submissionId The current submissionId + */ + constructor( + relationship: Relationship, + submissionId: string, + ) { + this.payload = { relationship, submissionId }; + } +} + /** * An ngrx action to remove an existing relationship */ diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts index fc047b1aae..23193e0f8e 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts @@ -4,7 +4,7 @@ import { debounceTime, filter, map, mergeMap, switchMap, take, tap } from 'rxjs/ import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { RelationshipService } from '../../../../../core/data/relationship.service'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../core/shared/operators'; -import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes, UpdateRelationshipAction } from './relationship.actions'; +import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes, UpdateRelationshipAction, UpdateRelationshipNameVariantAction } from './relationship.actions'; import { Item } from '../../../../../core/shared/item.model'; import { hasNoValue, hasValue, hasValueOperator } from '../../../../empty.util'; import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; @@ -18,6 +18,7 @@ import { Store } from '@ngrx/store'; import { ObjectCacheService } from '../../../../../core/cache/object-cache.service'; import { RequestService } from '../../../../../core/data/request.service'; import { ServerSyncBufferActionTypes } from '../../../../../core/cache/server-sync-buffer.actions'; +import { CommitPatchOperationsAction, JsonPatchOperationsActionTypes, PatchOperationsActions } from '../../../../../core/json-patch/json-patch-operations.actions'; const DEBOUNCE_TIME = 5000; @@ -91,8 +92,8 @@ export class RelationshipEffects { */ @Effect({ dispatch: false }) updateNameVariantsActions$ = this.actions$ .pipe( - ofType(RelationshipActionTypes.UPDATE_RELATIONSHIP), - map((action: UpdateRelationshipAction) => { + ofType(RelationshipActionTypes.UPDATE_NAME_VARIANT), + map((action: UpdateRelationshipNameVariantAction) => { const { item1, item2, relationshipType, submissionId, nameVariant } = action.payload; const identifier: string = this.createIdentifier(item1, item2, relationshipType); const inProgress = hasValue(this.debounceMap[identifier]); @@ -108,9 +109,17 @@ export class RelationshipEffects { ) ); + @Effect({ dispatch: false }) updateRelationshipActions$ = this.actions$ + .pipe( + ofType(RelationshipActionTypes.UPDATE_RELATIONSHIP), + map((action: UpdateRelationshipAction) => { + this.updateAfterPatchSubmissionId = action.payload.submissionId; + }) + ); + @Effect() commitServerSyncBuffer = this.actions$ .pipe( - ofType(ServerSyncBufferActionTypes.EMPTY), + ofType(ServerSyncBufferActionTypes.EMPTY, JsonPatchOperationsActionTypes.COMMIT_JSON_PATCH_OPERATIONS), filter(() => hasValue(this.updateAfterPatchSubmissionId)), switchMap(() => this.refreshWorkspaceItemInCache(this.updateAfterPatchSubmissionId)), map((submissionObject) => new SaveSubmissionSectionFormSuccessAction(this.updateAfterPatchSubmissionId, [submissionObject], false))