made sure added relationships are shown again, resolved issues with duplicate entries + other bug fixes

This commit is contained in:
lotte
2020-01-09 16:23:32 +01:00
parent 3e9eb9eedc
commit 23d4b16243
18 changed files with 155 additions and 83 deletions

View File

@@ -30,6 +30,7 @@ import { ItemDataService } from './item-data.service';
import { PaginatedList } from './paginated-list'; import { PaginatedList } from './paginated-list';
import { RemoteData, RemoteDataState } from './remote-data'; import { RemoteData, RemoteDataState } from './remote-data';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../shared/metadata.models';
const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;
@@ -412,4 +413,15 @@ export class RelationshipService extends DataService<Relationship> {
return update$; return update$;
} }
public toVirtualMetadata(relationship: Relationship, useLeft: boolean): MetadataValue {
const metadataValue = new MetadataValue();
metadataValue.authority = VIRTUAL_METADATA_PREFIX + relationship.id;
// What if there's no name variant?
if (useLeft) {
metadataValue.value = relationship.leftwardValue
} else {
metadataValue.value = relationship.rightwardValue
}
return metadataValue;
}
} }

View File

@@ -9,7 +9,7 @@
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: model"></ng-container> <ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: model"></ng-container>
<!-- Should be *ngIf instead of class d-none, but that breaks the #componentViewContainer reference--> <!-- Should be *ngIf instead of class d-none, but that breaks the #componentViewContainer reference-->
<div [ngClass]="{'form-row': model.hasLanguages || hasRelationLookup, 'd-none': relationshipValue$ | async}"> <div [ngClass]="{'form-row': model.hasLanguages || isRelationship, 'd-none': relationshipValue$ | async}">
<div [ngClass]="getClass('grid', 'control')"> <div [ngClass]="getClass('grid', 'control')">
<ng-container #componentViewContainer></ng-container> <ng-container #componentViewContainer></ng-container>
@@ -37,7 +37,7 @@
</select> </select>
</div> </div>
<div *ngIf="hasRelationLookup && context?.index < 1" class="col-auto text-center"> <div *ngIf="isRelationship && context?.index < 1" class="col-auto text-center">
<button class="btn btn-secondary" <button class="btn btn-secondary"
type="submit" type="submit"
ngbTooltip="{{'form.lookup-help' | translate}}" ngbTooltip="{{'form.lookup-help' | translate}}"
@@ -55,6 +55,7 @@
[submissionItem]="item" [submissionItem]="item"
[listId]="listId" [listId]="listId"
[metadataFields]="model.metadataFields" [metadataFields]="model.metadataFields"
[submissionId]="model.submissionId"
[relationshipOptions]="model.relationship"> [relationshipOptions]="model.relationship">
</ds-existing-metadata-list-element> </ds-existing-metadata-list-element>
</ng-container> </ng-container>

View File

@@ -1,5 +1,6 @@
import { import {
ChangeDetectionStrategy, ChangeDetectorRef, ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
ContentChildren, ContentChildren,
@@ -16,7 +17,7 @@ import {
ViewChild, ViewChild,
ViewContainerRef ViewContainerRef
} from '@angular/core'; } from '@angular/core';
import { FormArray, FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { import {
DYNAMIC_FORM_CONTROL_TYPE_ARRAY, DYNAMIC_FORM_CONTROL_TYPE_ARRAY,
@@ -29,7 +30,7 @@ import {
DYNAMIC_FORM_CONTROL_TYPE_SELECT, DYNAMIC_FORM_CONTROL_TYPE_SELECT,
DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA, DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA,
DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER, DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER,
DynamicDatePickerModel, DynamicFormArrayModel, DynamicDatePickerModel,
DynamicFormControl, DynamicFormControl,
DynamicFormControlContainerComponent, DynamicFormControlContainerComponent,
DynamicFormControlEvent, DynamicFormControlEvent,
@@ -50,10 +51,7 @@ import {
DynamicNGBootstrapTimePickerComponent DynamicNGBootstrapTimePickerComponent
} from '@ng-dynamic-forms/ui-ng-bootstrap'; } from '@ng-dynamic-forms/ui-ng-bootstrap';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import { ReorderableRelationship } from './existing-metadata-list-element/existing-metadata-list-element.component';
Reorderable,
ReorderableRelationship
} from './existing-metadata-list-element/existing-metadata-list-element.component';
import { DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD } from './models/typeahead/dynamic-typeahead.model'; import { DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD } from './models/typeahead/dynamic-typeahead.model';
import { DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; import { DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model';
@@ -75,8 +73,8 @@ import { DsDynamicFormArrayComponent } from './models/array-group/dynamic-form-a
import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components'; import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components';
import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/relation-group/dynamic-relation-group.model'; 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 { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component';
import { map, startWith, switchMap, take } from 'rxjs/operators'; import { map, switchMap, take } from 'rxjs/operators';
import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { SearchResult } from '../../../search/search-result.model'; import { SearchResult } from '../../../search/search-result.model';
import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
@@ -95,10 +93,10 @@ import { SubmissionObjectDataService } from '../../../../core/submission/submiss
import { SubmissionObject } from '../../../../core/submission/models/submission-object.model'; import { SubmissionObject } from '../../../../core/submission/models/submission-object.model';
import { PaginatedList } from '../../../../core/data/paginated-list'; import { PaginatedList } from '../../../../core/data/paginated-list';
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { MetadataValue } from '../../../../core/shared/metadata.models'; import { MetadataValue } from '../../../../core/shared/metadata.models';
import { DynamicRowArrayModel } from './models/ds-dynamic-row-array-model'; import { FormService } from '../../form.service';
import { combineLatest as observableCombineLatest } from 'rxjs';
export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<DynamicFormControl> | null { export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<DynamicFormControl> | null {
switch (model.type) { switch (model.type) {
@@ -183,7 +181,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Input() layout = null as DynamicFormLayout; @Input() layout = null as DynamicFormLayout;
@Input() model: any; @Input() model: any;
relationshipValue$: Observable<ReorderableRelationship>; relationshipValue$: Observable<ReorderableRelationship>;
hasRelationLookup: boolean; isRelationship: boolean;
modalRef: NgbModalRef; modalRef: NgbModalRef;
item: Item; item: Item;
listId: string; listId: string;
@@ -221,7 +219,8 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
private zone: NgZone, private zone: NgZone,
private store: Store<AppState>, private store: Store<AppState>,
private submissionObjectService: SubmissionObjectDataService, private submissionObjectService: SubmissionObjectDataService,
private ref: ChangeDetectorRef private ref: ChangeDetectorRef,
private formService: FormService
) { ) {
super(componentFactoryResolver, layoutService, validationService); super(componentFactoryResolver, layoutService, validationService);
} }
@@ -230,42 +229,32 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
* Sets up the necessary variables for when this control can be used to add relationships to the submitted item * Sets up the necessary variables for when this control can be used to add relationships to the submitted item
*/ */
ngOnInit(): void { ngOnInit(): void {
this.hasRelationLookup = hasValue(this.model.relationship); this.isRelationship = hasValue(this.model.relationship);
if (this.hasRelationLookup) { if (this.isRelationship) {
this.listId = 'list-' + this.model.relationship.relationshipType; this.listId = 'list-' + this.model.relationship.relationshipType;
const item$ = this.submissionObjectService this.setItem();
.findById(this.model.submissionId).pipe( const value = Object.assign(new MetadataValue(), this.model.value);
getAllSucceededRemoteData(), if (hasValue(value) && value.isVirtual) {
getRemoteDataPayload(), const relationship$ = this.relationshipService.findById(value.virtualValue)
switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>) .pipe(
.pipe( getAllSucceededRemoteData(),
getAllSucceededRemoteData(), getRemoteDataPayload());
getRemoteDataPayload() this.relationshipValue$ = relationship$.pipe(
switchMap((relationship: Relationship) =>
relationship.leftItem.pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
map((leftItem: Item) => {
return new ReorderableRelationship(relationship, leftItem.uuid !== this.item.uuid, this.relationshipService)
}),
) )
) )
); );
this.subs.push(item$.subscribe((item) => this.item = item));
const value = Object.assign(new MetadataValue(), this.model.value);
if (hasValue(value) && value.isVirtual) {
this.relationshipValue$ = this.relationshipService.findById(value.virtualValue)
.pipe(
getAllSucceededRemoteData(),
getRemoteDataPayload(),
switchMap((relationship: Relationship) =>
relationship.leftItem.pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
map((leftItem: Item) => {
return new ReorderableRelationship(relationship, leftItem.uuid !== this.item.uuid, this.relationshipService)
}),
)
)
);
} }
}
this.relationService.getRelatedItemsByLabel(this.item, this.model.relationship.relationshipType).pipe( if (this.model.relationshipConfig) {
this.setItem();
this.relationService.getRelatedItemsByLabel(this.item, this.model.relationshipConfig.relationshipType).pipe(
map((items: RemoteData<PaginatedList<Item>>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))), map((items: RemoteData<PaginatedList<Item>>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))),
).subscribe((relatedItems: Array<SearchResult<Item>>) => this.selectableListService.select(this.listId, relatedItems)); ).subscribe((relatedItems: Array<SearchResult<Item>>) => this.selectableListService.select(this.listId, relatedItems));
} }
@@ -316,6 +305,11 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
* Open a modal where the user can select relationships to be added to item being submitted * Open a modal where the user can select relationships to be added to item being submitted
*/ */
openLookup() { openLookup() {
// const event = this.createDynamicFormControlEvent(new CustomEvent('open'), 'change');
// event.control = this.control;
// event.model = this.model;
// this.onChange(event);
this.formService.removeForm(this.formId);
this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent, { this.modalRef = this.modalService.open(DsDynamicLookupRelationModalComponent, {
size: 'lg' size: 'lg'
}); });
@@ -328,6 +322,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
modalComp.relationshipOptions = this.model.relationship; modalComp.relationshipOptions = this.model.relationship;
modalComp.metadataFields = this.model.metadataFields; modalComp.metadataFields = this.model.metadataFields;
modalComp.item = this.item; modalComp.item = this.item;
modalComp.submissionId = this.model.submissionId;
} }
/** /**
@@ -338,4 +333,20 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
.filter((sub) => hasValue(sub)) .filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe()); .forEach((sub) => sub.unsubscribe());
} }
private setItem() {
const item$ = this.submissionObjectService
.findById(this.model.submissionId).pipe(
getAllSucceededRemoteData(),
getRemoteDataPayload(),
switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>)
.pipe(
getAllSucceededRemoteData(),
getRemoteDataPayload()
)
)
);
this.subs.push(item$.subscribe((item) => this.item = item));
}
} }

View File

@@ -8,5 +8,5 @@
[ngClass]="[getClass(model, 'element', 'host'), getClass(model, 'grid', 'host')]" [ngClass]="[getClass(model, 'element', 'host'), getClass(model, 'grid', 'host')]"
[templates]="templates" [templates]="templates"
(dfBlur)="onEvent($event, 'blur')" (dfBlur)="onEvent($event, 'blur')"
(dfChange)="onEvent($event, 'change')" (dfChange)="onEvent($event, 'change'); log('test')"
(dfFocus)="onEvent($event, 'focus')"></ds-dynamic-form-control-container> (dfFocus)="onEvent($event, 'focus')"></ds-dynamic-form-control-container>

View File

@@ -37,4 +37,8 @@ export class DsDynamicFormComponent extends DynamicFormComponent {
constructor(protected formService: FormBuilderService, protected layoutService: DynamicFormLayoutService) { constructor(protected formService: FormBuilderService, protected layoutService: DynamicFormLayoutService) {
super(formService, layoutService); super(formService, layoutService);
} }
log(t: string) {
console.log(t);
}
} }

View File

@@ -125,6 +125,7 @@ export class ExistingMetadataListElementComponent implements OnChanges, OnDestro
@Input() reoRel: ReorderableRelationship; @Input() reoRel: ReorderableRelationship;
@Input() metadataFields: string[]; @Input() metadataFields: string[];
@Input() relationshipOptions: RelationshipOptions; @Input() relationshipOptions: RelationshipOptions;
@Input() submissionId: string;
metadataRepresentation: MetadataRepresentation; metadataRepresentation: MetadataRepresentation;
relatedItem: Item; relatedItem: Item;
@@ -161,7 +162,7 @@ export class ExistingMetadataListElementComponent implements OnChanges, OnDestro
removeSelection() { removeSelection() {
this.selectableListService.deselectSingle(this.listId, Object.assign(new ItemSearchResult(), { indexableObject: this.relatedItem })); this.selectableListService.deselectSingle(this.listId, Object.assign(new ItemSearchResult(), { indexableObject: this.relatedItem }));
this.store.dispatch(new RemoveRelationshipAction(this.submissionItem, this.relatedItem, this.relationshipOptions.relationshipType)) this.store.dispatch(new RemoveRelationshipAction(this.submissionItem, this.relatedItem, this.relationshipOptions.relationshipType, this.submissionId))
} }
/** /**

View File

@@ -159,7 +159,6 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent imple
if (reorderable.hasMoved) { if (reorderable.hasMoved) {
const prevIndex = reorderable.oldIndex; const prevIndex = reorderable.oldIndex;
reorderable.update().pipe(take(1)).subscribe((v) => { reorderable.update().pipe(take(1)).subscribe((v) => {
console.log(reorderable, index);
if (reorderable instanceof ReorderableFormFieldMetadataValue) { if (reorderable instanceof ReorderableFormFieldMetadataValue) {
const reoMD = reorderable as ReorderableFormFieldMetadataValue; const reoMD = reorderable as ReorderableFormFieldMetadataValue;
const mdl = Object.assign({}, reoMD.model, { value: reoMD.metadataValue }); const mdl = Object.assign({}, reoMD.model, { value: reoMD.metadataValue });
@@ -189,9 +188,10 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent imple
if (hasNoValue($event.context)) { if (hasNoValue($event.context)) {
const context = Object.assign({}, $event.context, { index: this.reorderables.length }); const context = Object.assign({}, $event.context, { index: this.reorderables.length });
event = Object.assign({}, $event, { context }); event = Object.assign({}, $event, { context });
super.onChange(event);
} else { } else {
this.updateReorderables(); this.updateReorderables();
} }
super.onChange(event);
} }
} }

View File

@@ -1,10 +1,11 @@
import { DynamicFormArrayModel, DynamicFormArrayModelConfig, DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core'; import { DynamicFormArrayModel, DynamicFormArrayModelConfig, DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core';
import { RelationshipOptions } from '../../models/relationship-options.model';
export interface DynamicRowArrayModelConfig extends DynamicFormArrayModelConfig { export interface DynamicRowArrayModelConfig extends DynamicFormArrayModelConfig {
notRepeatable: boolean; notRepeatable: boolean;
required: boolean; required: boolean;
submissionId: string; submissionId: string;
hasRelationship: boolean; relationshipConfig: RelationshipOptions;
metadataKey: string; metadataKey: string;
} }
@@ -12,7 +13,7 @@ export class DynamicRowArrayModel extends DynamicFormArrayModel {
@serializable() notRepeatable = false; @serializable() notRepeatable = false;
@serializable() required = false; @serializable() required = false;
@serializable() submissionId: string; @serializable() submissionId: string;
@serializable() hasRelationship: boolean; @serializable() relationshipConfig: RelationshipOptions;
@serializable() metadataKey: string; @serializable() metadataKey: string;
isRowArray = true; isRowArray = true;
@@ -21,7 +22,7 @@ export class DynamicRowArrayModel extends DynamicFormArrayModel {
this.notRepeatable = config.notRepeatable; this.notRepeatable = config.notRepeatable;
this.required = config.required; this.required = config.required;
this.submissionId = config.submissionId; this.submissionId = config.submissionId;
this.hasRelationship = config.hasRelationship; this.relationshipConfig = config.relationshipConfig;
this.metadataKey = config.metadataKey; this.metadataKey = config.metadataKey;
} }
} }

View File

@@ -45,6 +45,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
subMap: { subMap: {
[uuid: string]: Subscription [uuid: string]: Subscription
} = {}; } = {};
submissionId: string;
constructor( constructor(
public modal: NgbActiveModal, public modal: NgbActiveModal,
@@ -92,7 +93,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
obs obs
.subscribe((arr: any[]) => { .subscribe((arr: any[]) => {
return arr.forEach((object: any) => { return arr.forEach((object: any) => {
this.store.dispatch(new AddRelationshipAction(this.item, object.item, this.relationshipOptions.relationshipType, object.nameVariant)); this.store.dispatch(new AddRelationshipAction(this.item, object.item, this.relationshipOptions.relationshipType, this.submissionId, object.nameVariant));
} }
); );
}) })
@@ -103,14 +104,14 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
const nameVariant$ = this.relationshipService.getNameVariant(this.listId, sri.indexableObject.uuid); const nameVariant$ = this.relationshipService.getNameVariant(this.listId, sri.indexableObject.uuid);
this.subMap[sri.indexableObject.uuid] = nameVariant$.pipe( this.subMap[sri.indexableObject.uuid] = nameVariant$.pipe(
skip(1), skip(1),
).subscribe((nameVariant: string) => this.store.dispatch(new UpdateRelationshipAction(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, nameVariant))) ).subscribe((nameVariant: string) => this.store.dispatch(new UpdateRelationshipAction(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, this.submissionId, nameVariant)))
} }
deselect(...selectableObjects: Array<SearchResult<Item>>) { deselect(...selectableObjects: Array<SearchResult<Item>>) {
this.zone.runOutsideAngular( this.zone.runOutsideAngular(
() => selectableObjects.forEach((object) => { () => selectableObjects.forEach((object) => {
this.subMap[object.indexableObject.uuid].unsubscribe(); this.subMap[object.indexableObject.uuid].unsubscribe();
this.store.dispatch(new RemoveRelationshipAction(this.item, object.indexableObject, this.relationshipOptions.relationshipType)); this.store.dispatch(new RemoveRelationshipAction(this.item, object.indexableObject, this.relationshipOptions.relationshipType, this.submissionId));
}) })
); );
} }

View File

@@ -22,6 +22,7 @@ export class AddRelationshipAction implements Action {
item1: Item; item1: Item;
item2: Item; item2: Item;
relationshipType: string; relationshipType: string;
submissionId: string;
nameVariant: string; nameVariant: string;
}; };
@@ -37,9 +38,10 @@ export class AddRelationshipAction implements Action {
item1: Item, item1: Item,
item2: Item, item2: Item,
relationshipType: string, relationshipType: string,
submissionId: string,
nameVariant?: string nameVariant?: string
) { ) {
this.payload = { item1, item2, relationshipType, nameVariant }; this.payload = { item1, item2, relationshipType, submissionId, nameVariant };
} }
} }
@@ -50,6 +52,7 @@ export class UpdateRelationshipAction implements Action {
item1: Item; item1: Item;
item2: Item; item2: Item;
relationshipType: string; relationshipType: string;
submissionId: string;
nameVariant: string; nameVariant: string;
}; };
@@ -65,9 +68,10 @@ export class UpdateRelationshipAction implements Action {
item1: Item, item1: Item,
item2: Item, item2: Item,
relationshipType: string, relationshipType: string,
submissionId: string,
nameVariant?: string nameVariant?: string
) { ) {
this.payload = { item1, item2, relationshipType, nameVariant }; this.payload = { item1, item2, relationshipType, submissionId, nameVariant };
} }
} }
@@ -81,6 +85,7 @@ export class RemoveRelationshipAction implements Action {
item1: Item; item1: Item;
item2: Item; item2: Item;
relationshipType: string; relationshipType: string;
submissionId: string;
}; };
/** /**
@@ -93,8 +98,10 @@ export class RemoveRelationshipAction implements Action {
constructor( constructor(
item1: Item, item1: Item,
item2: Item, item2: Item,
relationshipType: string) { relationshipType: string,
this.payload = { item1, item2, relationshipType }; submissionId: string
) {
this.payload = { item1, item2, relationshipType, submissionId };
} }
} }

View File

@@ -1,15 +1,20 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, Effect, ofType } from '@ngrx/effects';
import { debounceTime, map, mergeMap, take, tap } from 'rxjs/operators'; import { debounceTime, map, mergeMap, switchMap, take } from 'rxjs/operators';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { RelationshipService } from '../../../../../core/data/relationship.service'; import { RelationshipService } from '../../../../../core/data/relationship.service';
import { getSucceededRemoteData } from '../../../../../core/shared/operators'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../core/shared/operators';
import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes, RemoveRelationshipAction, UpdateRelationshipAction } from './relationship.actions'; import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes, UpdateRelationshipAction } from './relationship.actions';
import { Item } from '../../../../../core/shared/item.model'; import { Item } from '../../../../../core/shared/item.model';
import { hasNoValue, hasValue, hasValueOperator } from '../../../../empty.util'; import { hasNoValue, hasValue, hasValueOperator } from '../../../../empty.util';
import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model'; import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model';
import { RelationshipType } from '../../../../../core/shared/item-relationships/relationship-type.model'; import { RelationshipType } from '../../../../../core/shared/item-relationships/relationship-type.model';
import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service'; import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service';
import { SubmissionObjectDataService } from '../../../../../core/submission/submission-object-data.service';
import { SaveSubmissionSectionFormSuccessAction } from '../../../../../submission/objects/submission-objects.actions';
import { SubmissionObject } from '../../../../../core/submission/models/submission-object.model';
import { SubmissionState } from '../../../../../submission/submission.reducers';
import { Store } from '@ngrx/store';
const DEBOUNCE_TIME = 5000; const DEBOUNCE_TIME = 5000;
@@ -40,7 +45,7 @@ export class RelationshipEffects {
.pipe( .pipe(
ofType(RelationshipActionTypes.ADD_RELATIONSHIP, RelationshipActionTypes.REMOVE_RELATIONSHIP), ofType(RelationshipActionTypes.ADD_RELATIONSHIP, RelationshipActionTypes.REMOVE_RELATIONSHIP),
map((action: RelationshipAction) => { map((action: RelationshipAction) => {
const { item1, item2, relationshipType } = action.payload; const { item1, item2, submissionId, relationshipType } = action.payload;
const identifier: string = this.createIdentifier(item1, item2, relationshipType); const identifier: string = this.createIdentifier(item1, item2, relationshipType);
if (hasNoValue(this.debounceMap[identifier])) { if (hasNoValue(this.debounceMap[identifier])) {
this.initialActionMap[identifier] = action.type; this.initialActionMap[identifier] = action.type;
@@ -57,13 +62,15 @@ export class RelationshipEffects {
nameVariant = this.nameVariantUpdates[identifier]; nameVariant = this.nameVariantUpdates[identifier];
delete this.nameVariantUpdates[identifier]; delete this.nameVariantUpdates[identifier];
} }
this.addRelationship(item1, item2, relationshipType, nameVariant) this.addRelationship(item1, item2, relationshipType, submissionId, nameVariant);
} else { } else {
this.removeRelationship(item1, item2, relationshipType); this.removeRelationship(item1, item2, relationshipType, submissionId);
} }
} }
delete this.debounceMap[identifier]; delete this.debounceMap[identifier];
delete this.initialActionMap[identifier]; delete this.initialActionMap[identifier];
} }
) )
} else { } else {
@@ -98,6 +105,8 @@ export class RelationshipEffects {
constructor(private actions$: Actions, constructor(private actions$: Actions,
private relationshipService: RelationshipService, private relationshipService: RelationshipService,
private relationshipTypeService: RelationshipTypeService, private relationshipTypeService: RelationshipTypeService,
private submissionObjectService: SubmissionObjectDataService,
private store: Store<SubmissionState>
) { ) {
} }
@@ -105,7 +114,7 @@ export class RelationshipEffects {
return `${item1.uuid}-${item2.uuid}-${relationshipType}`; return `${item1.uuid}-${item2.uuid}-${relationshipType}`;
} }
private addRelationship(item1: Item, item2: Item, relationshipType: string, nameVariant?: string) { private addRelationship(item1: Item, item2: Item, relationshipType: string, submissionId: string, nameVariant?: string) {
const type1: string = item1.firstMetadataValue('relationship.type'); const type1: string = item1.firstMetadataValue('relationship.type');
const type2: string = item2.firstMetadataValue('relationship.type'); const type2: string = item2.firstMetadataValue('relationship.type');
return this.relationshipTypeService.getRelationshipTypeByLabelAndTypes(relationshipType, type1, type2) return this.relationshipTypeService.getRelationshipTypeByLabelAndTypes(relationshipType, type1, type2)
@@ -119,16 +128,39 @@ export class RelationshipEffects {
} }
} }
) )
).pipe(take(1)) ).pipe(take(1), switchMap(() => this.submissionObjectService.findById(submissionId).pipe(getSucceededRemoteData(), getRemoteDataPayload()))
.subscribe(); ).subscribe((submissionObject: SubmissionObject) => this.store.dispatch(new SaveSubmissionSectionFormSuccessAction(submissionId, [submissionObject], false)));
} }
private removeRelationship(item1: Item, item2: Item, relationshipType: string) { private removeRelationship(item1: Item, item2: Item, relationshipType: string, submissionId: string) {
this.relationshipService.getRelationshipByItemsAndLabel(item1, item2, relationshipType).pipe( this.relationshipService.getRelationshipByItemsAndLabel(item1, item2, relationshipType).pipe(
take(1), take(1),
hasValueOperator(), hasValueOperator(),
mergeMap((relationship: Relationship) => this.relationshipService.deleteRelationship(relationship.id)), mergeMap((relationship: Relationship) => this.relationshipService.deleteRelationship(relationship.id)),
take(1) take(1),
).subscribe(); switchMap(() => this.submissionObjectService.findById(submissionId).pipe(getSucceededRemoteData(), getRemoteDataPayload()))
).subscribe((submissionObject: SubmissionObject) => this.store.dispatch(new SaveSubmissionSectionFormSuccessAction(submissionId, [submissionObject])));
} }
// private addAsMetadataInStore(submissionID: string, sectionID: string, metadataField: string, relationship: Relationship, repeatable: boolean, relationshipType: string) {
// const sectionData$: Observable<WorkspaceitemSectionDataType> = this.sectionsService.getSectionData(submissionID, sectionID);
// observableCombineLatest(
// sectionData$.pipe(take(1)),
// relationship.relationshipType.pipe(getSucceededRemoteData(), getRemoteDataPayload())
// ).subscribe(
// ([sectionData, relType]: [WorkspaceitemSectionDataType, RelationshipType]) => {
// const useLeft = relType.rightwardType === relationshipType;
// const value = this.relationshipService.toVirtualMetadata(relationship, useLeft);
// let values;
// if (repeatable) {
// const existingValues: FormFieldMetadataValueObject[] = sectionData[metadataField];
// values = [...existingValues, value];
// } else {
// values = [value];
// }
// sectionData[metadataField] = values;
// this.sectionsService.updateSectionData(submissionID, sectionID, sectionData);
// }
// );
// }
} }

View File

@@ -260,7 +260,7 @@ describe('FormBuilderService test suite', () => {
id: 'testFormRowArray', id: 'testFormRowArray',
initialCount: 5, initialCount: 5,
notRepeatable: false, notRepeatable: false,
hasRelationship: false, relationshipConfig: undefined,
submissionId: '1234', submissionId: '1234',
groupFactory: () => { groupFactory: () => {
return [ return [

View File

@@ -51,7 +51,7 @@ export abstract class FieldParser {
label: this.configData.label, label: this.configData.label,
initialCount: this.getInitArrayIndex(), initialCount: this.getInitArrayIndex(),
notRepeatable: !this.configData.repeatable, notRepeatable: !this.configData.repeatable,
hasRelationship: isNotEmpty(this.configData.selectableRelationship), relationshipConfig: this.configData.selectableRelationship,
required: isNotEmpty(this.configData.mandatory), required: isNotEmpty(this.configData.mandatory),
submissionId: this.submissionId, submissionId: this.submissionId,
metadataKey, metadataKey,

View File

@@ -12,7 +12,7 @@
(dfFocus)="onFocus($event)"> (dfFocus)="onFocus($event)">
<ng-template modelType="ARRAY" let-group let-index="index" let-context="context"> <ng-template modelType="ARRAY" let-group let-index="index" let-context="context">
<!--Array with repeatable items--> <!--Array with repeatable items-->
<div *ngIf="!context.notRepeatable && (!context.hasRelationship || (context.hasRelationship && index < 1))" <div *ngIf="!context.notRepeatable && (!context.relationshipConfig || (context.relationshipConfig && index < 1))"
class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end"> class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end">
<div class="btn-group" role="group" aria-label="Add and remove button"> <div class="btn-group" role="group" aria-label="Add and remove button">
<button type="button" class="btn btn-secondary" <button type="button" class="btn btn-secondary"
@@ -29,7 +29,7 @@
</div> </div>
<!--Array with non repeatable items - Only delete button--> <!--Array with non repeatable items - Only delete button-->
<div *ngIf="context.notRepeatable && group.context.groups.length > 1 || (context.hasRelationship && index > 0 && !group.group[0]?.value?.isVirtual)" <div *ngIf="context.notRepeatable && group.context.groups.length > 1 || (context.relationshipConfig && index > 0 && !group.group[0]?.value?.isVirtual)"
class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end"> class="col-xs-2 d-flex flex-column justify-content-sm-start align-items-end">
<div class="btn-group" role="group" aria-label="Remove button"> <div class="btn-group" role="group" aria-label="Remove button">
<button type="button" class="btn btn-secondary" <button type="button" class="btn btn-secondary"

View File

@@ -76,7 +76,7 @@ const rowArrayQualdropConfig = {
id: 'row_QUALDROP_GROUP', id: 'row_QUALDROP_GROUP',
initialCount: 1, initialCount: 1,
notRepeatable: true, notRepeatable: true,
hasRelationship: false, relationshipConfig: undefined,
groupFactory: () => { groupFactory: () => {
return [MockQualdropModel]; return [MockQualdropModel];
}, },

View File

@@ -33,8 +33,8 @@ describe('ObjectCollectionComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
fixture = TestBed.createComponent(ObjectCollectionComponent); fixture = TestBed.createComponent(ObjectCollectionComponent);
objectCollectionComponent = fixture.componentInstance; objectCollectionComponent = fixture.componentInstance;
})); }));
it('should only show the grid component when the viewmode is set to grid', () => { it('should only show the grid component when the viewmode is set to grid', () => {
objectCollectionComponent.currentMode$ = observableOf(ViewMode.GridElement); objectCollectionComponent.currentMode$ = observableOf(ViewMode.GridElement);

View File

@@ -378,6 +378,7 @@ export class SaveSubmissionFormSuccessAction implements Action {
payload: { payload: {
submissionId: string; submissionId: string;
submissionObject: SubmissionObject[]; submissionObject: SubmissionObject[];
notify?: boolean
}; };
/** /**
@@ -388,8 +389,8 @@ export class SaveSubmissionFormSuccessAction implements Action {
* @param submissionObject * @param submissionObject
* the submission's Object * the submission's Object
*/ */
constructor(submissionId: string, submissionObject: SubmissionObject[]) { constructor(submissionId: string, submissionObject: SubmissionObject[], notify?: boolean) {
this.payload = { submissionId, submissionObject }; this.payload = { submissionId, submissionObject, notify };
} }
} }
@@ -435,6 +436,7 @@ export class SaveSubmissionSectionFormSuccessAction implements Action {
payload: { payload: {
submissionId: string; submissionId: string;
submissionObject: SubmissionObject[]; submissionObject: SubmissionObject[];
notify?: boolean
}; };
/** /**
@@ -445,8 +447,8 @@ export class SaveSubmissionSectionFormSuccessAction implements Action {
* @param submissionObject * @param submissionObject
* the submission's Object * the submission's Object
*/ */
constructor(submissionId: string, submissionObject: SubmissionObject[]) { constructor(submissionId: string, submissionObject: SubmissionObject[], notify?: boolean) {
this.payload = { submissionId, submissionObject }; this.payload = { submissionId, submissionObject, notify };
} }
} }

View File

@@ -136,7 +136,7 @@ export class SubmissionObjectEffects {
ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_SUCCESS, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_SUCCESS), ofType(SubmissionObjectActionTypes.SAVE_SUBMISSION_FORM_SUCCESS, SubmissionObjectActionTypes.SAVE_SUBMISSION_SECTION_FORM_SUCCESS),
withLatestFrom(this.store$), withLatestFrom(this.store$),
map(([action, currentState]: [SaveSubmissionFormSuccessAction | SaveSubmissionSectionFormSuccessAction, any]) => { map(([action, currentState]: [SaveSubmissionFormSuccessAction | SaveSubmissionSectionFormSuccessAction, any]) => {
return this.parseSaveResponse((currentState.submission as SubmissionState).objects[action.payload.submissionId], action.payload.submissionObject, action.payload.submissionId); return this.parseSaveResponse((currentState.submission as SubmissionState).objects[action.payload.submissionId], action.payload.submissionObject, action.payload.submissionId, action.payload.notify);
}), }),
mergeMap((actions) => observableFrom(actions))); mergeMap((actions) => observableFrom(actions)));