diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html index 8edcb6808e..1a7cc2e2df 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html @@ -1,16 +1,15 @@ - -
-
{{getRelationshipMessageKey(relationshipLabel) | translate}}
- -
-
- +
{{getRelationshipMessageKey() | async | translate}}
+ + + + + + -
+
no relationships
diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index bc2fcda3a9..9b2bb3c992 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -1,27 +1,26 @@ -import { EditRelationshipListComponent } from './edit-relationship-list.component'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; -import { ResourceType } from '../../../../core/shared/resource-type'; -import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; -import { of as observableOf } from 'rxjs/internal/observable/of'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { Item } from '../../../../core/shared/item.model'; -import { PaginatedList } from '../../../../core/data/paginated-list'; -import { PageInfo } from '../../../../core/shared/page-info.model'; -import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; -import { SharedModule } from '../../../../shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; -import { RelationshipService } from '../../../../core/data/relationship.service'; -import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { By } from '@angular/platform-browser'; +import {EditRelationshipListComponent} from './edit-relationship-list.component'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model'; +import {Relationship} from '../../../../core/shared/item-relationships/relationship.model'; +import {of as observableOf} from 'rxjs/internal/observable/of'; +import {RemoteData} from '../../../../core/data/remote-data'; +import {Item} from '../../../../core/shared/item.model'; +import {PaginatedList} from '../../../../core/data/paginated-list'; +import {PageInfo} from '../../../../core/shared/page-info.model'; +import {FieldChangeType} from '../../../../core/data/object-updates/object-updates.actions'; +import {SharedModule} from '../../../../shared/shared.module'; +import {TranslateModule} from '@ngx-translate/core'; +import {ObjectUpdatesService} from '../../../../core/data/object-updates/object-updates.service'; +import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {ItemType} from '../../../../core/shared/item-relationships/item-type.model'; let comp: EditRelationshipListComponent; let fixture: ComponentFixture; let de: DebugElement; let objectUpdatesService; -let relationshipService; +let entityTypeService; const url = 'http://test-url.com/test-url'; @@ -30,42 +29,65 @@ let author1; let author2; let fieldUpdate1; let fieldUpdate2; -let relationships; +let relationship1; +let relationship2; let relationshipType; +let entityType; +let relatedEntityType; describe('EditRelationshipListComponent', () => { beforeEach(async(() => { + relationshipType = Object.assign(new RelationshipType(), { id: '1', uuid: '1', leftwardType: 'isAuthorOfPublication', - rightwardType: 'isPublicationOfAuthor' + rightwardType: 'isPublicationOfAuthor', + leftType: observableOf(new RemoteData(false, false, true, undefined, entityType)), + rightType: observableOf(new RemoteData(false, false, true, undefined, relatedEntityType)), }); - relationships = [ - Object.assign(new Relationship(), { - self: url + '/2', - id: '2', - uuid: '2', - leftId: 'author1', - rightId: 'publication', - relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) - }), - Object.assign(new Relationship(), { - self: url + '/3', - id: '3', - uuid: '3', - leftId: 'author2', - rightId: 'publication', - relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) - }) - ]; + relationship1 = Object.assign(new Relationship(), { + self: url + '/2', + id: '2', + uuid: '2', + leftId: 'author1', + rightId: 'publication', + leftItem: observableOf(new RemoteData(false, false, true, undefined, item)), + rightItem: observableOf(new RemoteData(false, false, true, undefined, author1)), + relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) + }); + + relationship2 = Object.assign(new Relationship(), { + self: url + '/3', + id: '3', + uuid: '3', + leftId: 'author2', + rightId: 'publication', + leftItem: observableOf(new RemoteData(false, false, true, undefined, item)), + rightItem: observableOf(new RemoteData(false, false, true, undefined, author2)), + relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) + }); item = Object.assign(new Item(), { self: 'fake-item-url/publication', id: 'publication', uuid: 'publication', - relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships))) + relationships: observableOf(new RemoteData( + false, + false, + true, + undefined, + new PaginatedList(new PageInfo(), [relationship1, relationship2]) + )) + }); + + entityType = Object.assign(new ItemType(), { + id: 'entityType', + }); + + relatedEntityType = Object.assign(new ItemType(), { + id: 'relatedEntityType', }); author1 = Object.assign(new Item(), { @@ -88,17 +110,29 @@ describe('EditRelationshipListComponent', () => { objectUpdatesService = jasmine.createSpyObj('objectUpdatesService', { - getFieldUpdatesExclusive: observableOf({ + getFieldUpdates: observableOf({ [author1.uuid]: fieldUpdate1, [author2.uuid]: fieldUpdate2 }) } ); - relationshipService = jasmine.createSpyObj('relationshipService', + entityTypeService = jasmine.createSpyObj('entityTypeService', { - getRelatedItemsByLabel: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [author1, author2]))), - getItemRelationshipsByLabel: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), relationships))), + getEntityTypeByLabel: observableOf(new RemoteData( + false, + false, + true, + null, + entityType, + )), + getEntityTypeRelationships: observableOf(new RemoteData( + false, + false, + true, + null, + new PaginatedList(new PageInfo(), [relationshipType]), + )), } ); @@ -107,7 +141,6 @@ describe('EditRelationshipListComponent', () => { declarations: [EditRelationshipListComponent], providers: [ { provide: ObjectUpdatesService, useValue: objectUpdatesService }, - { provide: RelationshipService, useValue: relationshipService } ], schemas: [ NO_ERRORS_SCHEMA ] @@ -120,7 +153,7 @@ describe('EditRelationshipListComponent', () => { de = fixture.debugElement; comp.item = item; comp.url = url; - comp.relationshipLabel = relationshipType.leftwardType; + comp.relationshipType = relationshipType; fixture.detectChanges(); }); diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index 7645b2b4b7..13214e27cf 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -1,12 +1,15 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; -import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; -import { Observable } from 'rxjs/internal/Observable'; -import { FieldUpdate, FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer'; -import { RelationshipService } from '../../../../core/data/relationship.service'; -import { Item } from '../../../../core/shared/item.model'; -import { map, switchMap} from 'rxjs/operators'; -import { hasValue } from '../../../../shared/empty.util'; +import {Component, Input, OnInit} from '@angular/core'; +import {ObjectUpdatesService} from '../../../../core/data/object-updates/object-updates.service'; +import {Observable} from 'rxjs/internal/Observable'; +import {FieldUpdate, FieldUpdates} from '../../../../core/data/object-updates/object-updates.reducer'; +import {Item} from '../../../../core/shared/item.model'; +import {map, switchMap} from 'rxjs/operators'; +import {hasValue} from '../../../../shared/empty.util'; import {Relationship} from '../../../../core/shared/item-relationships/relationship.model'; +import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model'; +import {getRemoteDataPayload, getSucceededRemoteData} from '../../../../core/shared/operators'; +import {combineLatest as observableCombineLatest, combineLatest} from 'rxjs'; +import {ItemType} from '../../../../core/shared/item-relationships/item-type.model'; @Component({ selector: 'ds-edit-relationship-list', @@ -17,12 +20,15 @@ import {Relationship} from '../../../../core/shared/item-relationships/relations * A component creating a list of editable relationships of a certain type * The relationships are rendered as a list of related items */ -export class EditRelationshipListComponent implements OnInit, OnChanges { +export class EditRelationshipListComponent implements OnInit { + /** * The item to display related items for */ @Input() item: Item; + @Input() itemType: ItemType; + /** * The URL to the current page * Used to fetch updates for the current item from the store @@ -32,7 +38,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges { /** * The label of the relationship-type we're rendering a list for */ - @Input() relationshipLabel: string; + @Input() relationshipType: RelationshipType; /** * The FieldUpdates for the relationships in question @@ -41,48 +47,42 @@ export class EditRelationshipListComponent implements OnInit, OnChanges { constructor( protected objectUpdatesService: ObjectUpdatesService, - protected relationshipService: RelationshipService ) { } - ngOnInit(): void { - this.initUpdates(); - } - - ngOnChanges(changes: SimpleChanges): void { - this.initUpdates(); - } - /** - * Initialize the FieldUpdates using the related items + * Get the i18n message key for this relationship type */ - initUpdates() { - this.updates$ = this.getUpdatesByLabel(this.relationshipLabel); - } + public getRelationshipMessageKey(): Observable { - /** - * Get FieldUpdates for the relationships of a specific type - * @param label The relationship type's label - */ - public getUpdatesByLabel(label: string): Observable { - return this.relationshipService.getItemRelationshipsByLabel(this.item, label).pipe( - map((relationsRD) => relationsRD.payload.page.map((relationship) => - Object.assign(new Relationship(), relationship, {uuid: relationship.id}) - )), - switchMap((initialFields) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, initialFields)), + return this.getLabel().pipe( + map((label) => { + if (hasValue(label) && label.indexOf('Of') > -1) { + return `relationships.${label.substring(0, label.indexOf('Of') + 2)}` + } else { + return label; + } + }), ); } /** - * Get the i18n message key for a relationship - * @param label The relationship type's label + * Get the relevant label for this relationship type */ - public getRelationshipMessageKey(label: string): string { - if (hasValue(label) && label.indexOf('Of') > -1) { - return `relationships.${label.substring(0, label.indexOf('Of') + 2)}` - } else { - return label; - } + private getLabel(): Observable { + + return combineLatest([ + this.relationshipType.leftType, + this.relationshipType.rightType, + ].map((itemTypeRD) => itemTypeRD.pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + ))).pipe( + map((itemTypes) => [ + this.relationshipType.leftwardType, + this.relationshipType.rightwardType, + ][itemTypes.findIndex((itemType) => itemType.id === this.itemType.id)]), + ); } /** @@ -91,4 +91,27 @@ export class EditRelationshipListComponent implements OnInit, OnChanges { trackUpdate(index, update: FieldUpdate) { return update && update.field ? update.field.uuid : undefined; } + + ngOnInit(): void { + this.updates$ = this.item.relationships.pipe( + map((relationships) => relationships.payload.page.filter((relationship) => relationship)), + switchMap((itemRelationships) => + observableCombineLatest( + itemRelationships + .map((relationship) => relationship.relationshipType.pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + )) + ).pipe( + map((relationshipTypes) => itemRelationships.filter( + (relationship, index) => relationshipTypes[index].id === this.relationshipType.id) + ), + map((relationships) => relationships.map((relationship) => + Object.assign(new Relationship(), relationship, {uuid: relationship.id}) + )), + ) + ), + switchMap((initialFields) => this.objectUpdatesService.getFieldUpdates(this.url, initialFields)), + ); + } } diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.html b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.html index 245bf04051..7e61e8958f 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.html +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.html @@ -19,7 +19,9 @@  {{"item.edit.metadata.save-button" | translate}} -
- +
+
diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts index 1e678884b1..2030b07f50 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts @@ -22,10 +22,12 @@ import { ErrorResponse, RestResponse } from '../../../core/cache/response.models import { isNotEmptyOperator } from '../../../shared/empty.util'; import { RemoteData } from '../../../core/data/remote-data'; import { ObjectCacheService } from '../../../core/cache/object-cache.service'; -import { getSucceededRemoteData} from '../../../core/shared/operators'; +import {getRemoteDataPayload, getSucceededRemoteData} from '../../../core/shared/operators'; import { RequestService } from '../../../core/data/request.service'; import { Subscription } from 'rxjs/internal/Subscription'; -import { getRelationsByRelatedItemIds } from '../../simple/item-types/shared/item-relationships-utils'; +import {RelationshipType} from '../../../core/shared/item-relationships/relationship-type.model'; +import {ItemType} from '../../../core/shared/item-relationships/item-type.model'; +import {EntityTypeService} from '../../../core/data/entity-type.service'; @Component({ selector: 'ds-item-relationships', @@ -40,13 +42,14 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl /** * The labels of all different relations within this item */ - relationLabels$: Observable; + relationshipTypes$: Observable; /** * A subscription that checks when the item is deleted in cache and reloads the item by sending a new request * This is used to update the item in cache after relationships are deleted */ itemUpdateSubscription: Subscription; + entityType$: Observable; constructor( protected itemService: ItemDataService, @@ -59,7 +62,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl protected relationshipService: RelationshipService, protected objectCache: ObjectCacheService, protected requestService: RequestService, - protected cdRef: ChangeDetectorRef + protected entityTypeService: EntityTypeService, ) { super(itemService, objectUpdatesService, router, notificationsService, translateService, EnvConfig, route); } @@ -69,21 +72,13 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl */ ngOnInit(): void { super.ngOnInit(); - this.relationLabels$ = this.relationshipService.getItemRelationshipLabels(this.item); - this.initializeItemUpdate(); - } - - /** - * Update the item (and view) when it's removed in the request cache - */ - public initializeItemUpdate(): void { this.itemUpdateSubscription = this.requestService.hasByHrefObservable(this.item.self).pipe( filter((exists: boolean) => !exists), switchMap(() => this.itemService.findById(this.item.uuid)), getSucceededRemoteData(), ).subscribe((itemRD: RemoteData) => { this.item = itemRD.payload; - this.cdRef.detectChanges(); + this.initializeUpdates(); }); } @@ -91,8 +86,22 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl * Initialize the values and updates of the current item's relationship fields */ public initializeUpdates(): void { - this.updates$ = this.relationshipService.getRelatedItems(this.item).pipe( - switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdates(this.url, items)) + + this.entityType$ = this.entityTypeService.getEntityTypeByLabel( + this.item.firstMetadataValue('relationship.type') + ).pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + ); + + this.relationshipTypes$ = this.entityType$.pipe( + switchMap((entityType) => + this.entityTypeService.getEntityTypeRelationships(entityType.id).pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + map((relationshipTypes) => relationshipTypes.page), + ) + ), ); } @@ -142,8 +151,9 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl )) ), ).subscribe((responses: RestResponse[]) => { - this.displayNotifications(responses); - this.reset(); + this.itemUpdateSubscription.add(() => { + this.displayNotifications(responses); + }); }); } @@ -165,22 +175,12 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl } } - /** - * Re-initialize fields and subscriptions - */ - reset() { - this.initializeOriginalFields(); - this.initializeUpdates(); - this.initializeItemUpdate(); - } - /** * Sends all initial values of this item to the object updates service */ public initializeOriginalFields() { - this.relationshipService.getRelatedItems(this.item).pipe(take(1)).subscribe((items: Item[]) => { - this.objectUpdatesService.initialize(this.url, items, this.item.lastModified); - }); + const initialFields = []; + this.objectUpdatesService.initialize(this.url, initialFields, this.item.lastModified); } /** @@ -189,5 +189,4 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl ngOnDestroy(): void { this.itemUpdateSubscription.unsubscribe(); } - } diff --git a/src/app/+item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html b/src/app/+item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html index 8a0269d6b7..f5dab71884 100644 --- a/src/app/+item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html +++ b/src/app/+item-page/edit-item-page/virtual-metadata/virtual-metadata.component.html @@ -5,37 +5,33 @@