diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.scss b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.scss
index cbedd42280..898533a9f0 100644
--- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.scss
+++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.scss
@@ -20,14 +20,3 @@
}
-
-.relationship-row:not(.alert-danger) {
- padding: $alert-padding-y 0;
-}
-
-.relationship-row.alert-danger {
- margin-left: -$alert-padding-x;
- margin-right: -$alert-padding-x;
- margin-top: -1px;
- margin-bottom: -1px;
-}
diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts
index 8579d25fdf..2439eb4c63 100644
--- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts
+++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts
@@ -1,6 +1,6 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemRelationshipsComponent } from './item-relationships.component';
-import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
+import { ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { INotification, Notification } from '../../../shared/notifications/models/notification.model';
import { NotificationType } from '../../../shared/notifications/models/notification-type';
import { RouterStub } from '../../../shared/testing/router-stub';
@@ -24,8 +24,8 @@ import { FieldChangeType } from '../../../core/data/object-updates/object-update
import { RelationshipService } from '../../../core/data/relationship.service';
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
import { getTestScheduler } from 'jasmine-marbles';
-import { By } from '@angular/platform-browser';
import { RestResponse } from '../../../core/cache/response.models';
+import { RequestService } from '../../../core/data/request.service';
let comp: any;
let fixture: ComponentFixture;
@@ -33,6 +33,7 @@ let de: DebugElement;
let el: HTMLElement;
let objectUpdatesService;
let relationshipService;
+let requestService;
let objectCache;
const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
@@ -158,6 +159,13 @@ describe('ItemRelationshipsComponent', () => {
}
);
+ requestService = jasmine.createSpyObj('requestService',
+ {
+ removeByHrefSubstring: {},
+ hasByHrefObservable: observableOf(false)
+ }
+ );
+
objectCache = jasmine.createSpyObj('objectCache', {
remove: undefined
});
@@ -174,7 +182,9 @@ describe('ItemRelationshipsComponent', () => {
{ provide: NotificationsService, useValue: notificationsService },
{ provide: GLOBAL_CONFIG, useValue: { item: { edit: { undoTimeout: 10 } } } as any },
{ provide: RelationshipService, useValue: relationshipService },
- { provide: ObjectCacheService, useValue: objectCache }
+ { provide: ObjectCacheService, useValue: objectCache },
+ { provide: RequestService, useValue: requestService },
+ ChangeDetectorRef
], schemas: [
NO_ERRORS_SCHEMA
]
@@ -210,17 +220,6 @@ describe('ItemRelationshipsComponent', () => {
});
});
- describe('changeType is REMOVE', () => {
- beforeEach(() => {
- fieldUpdate1.changeType = FieldChangeType.REMOVE;
- fixture.detectChanges();
- });
- it('the div should have class alert-danger', () => {
- const element = de.queryAll(By.css('.relationship-row'))[1].nativeElement;
- expect(element.classList).toContain('alert-danger');
- });
- });
-
describe('submit', () => {
beforeEach(() => {
comp.submit();
@@ -229,6 +228,7 @@ describe('ItemRelationshipsComponent', () => {
it('it should delete the correct relationship and de-cache the current item', () => {
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationships[1].uuid);
expect(objectCache.remove).toHaveBeenCalledWith(item.self);
+ expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(item.self);
});
});
});
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 8e8a31f896..7db8756c78 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
@@ -1,8 +1,8 @@
-import { Component, Inject } from '@angular/core';
+import { ChangeDetectorRef, Component, Inject, OnDestroy } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
import { Observable } from 'rxjs/internal/Observable';
-import { map, switchMap, take, tap } from 'rxjs/operators';
+import { filter, map, switchMap, take } from 'rxjs/operators';
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
import { ItemDataService } from '../../../core/data/item-data.service';
@@ -20,6 +20,7 @@ import { RemoteData } from '../../../core/data/remote-data';
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
import { getSucceededRemoteData } from '../../../core/shared/operators';
import { RequestService } from '../../../core/data/request.service';
+import { Subscription } from 'rxjs/internal/Subscription';
@Component({
selector: 'ds-item-relationships',
@@ -29,13 +30,19 @@ import { RequestService } from '../../../core/data/request.service';
/**
* Component for displaying an item's relationships edit page
*/
-export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
+export class ItemRelationshipsComponent extends AbstractItemUpdateComponent implements OnDestroy {
/**
* The labels of all different relations within this item
*/
relationLabels$: 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;
+
constructor(
protected itemService: ItemDataService,
protected objectUpdatesService: ObjectUpdatesService,
@@ -46,7 +53,8 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
protected route: ActivatedRoute,
protected relationshipService: RelationshipService,
protected objectCache: ObjectCacheService,
- protected requestService: RequestService
+ protected requestService: RequestService,
+ protected cdRef: ChangeDetectorRef
) {
super(itemService, objectUpdatesService, router, notificationsService, translateService, EnvConfig, route);
}
@@ -57,6 +65,16 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
ngOnInit(): void {
super.ngOnInit();
this.relationLabels$ = this.relationshipService.getItemRelationshipLabels(this.item);
+
+ // Update the item (and view) when it's removed in the request cache
+ 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();
+ });
}
/**
@@ -104,13 +122,12 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
switchMap((removedIds: string[]) => observableZip(...removedIds.map((uuid: string) => this.relationshipService.deleteRelationship(uuid)))),
map((responses: RestResponse[]) => responses.filter((response: RestResponse) => response.isSuccessful))
).subscribe((responses: RestResponse[]) => {
- // Make sure the lists are up-to-date and send a notification that the removal was successful
- // TODO: Fix lists refreshing correctly
+ // Remove the item's cache to make sure the lists are reloaded with the newest values
this.objectCache.remove(this.item.self);
this.requestService.removeByHrefSubstring(this.item.self);
- // this.itemService.findById(this.item.id).pipe(getSucceededRemoteData(), take(1)).subscribe((itemRD: RemoteData
- ) => this.item = itemRD.payload);
this.initializeOriginalFields();
this.initializeUpdates();
+ // Send a notification that the removal was successful
this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved'));
});
}
@@ -125,33 +142,10 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent {
}
/**
- * Transform the item's relationships of a specific type into related items
- * @param label The relationship type's label
+ * Unsubscribe from the item update when the component is destroyed
*/
- public getRelatedItemsByLabel(label: string): Observable
- {
- return this.relationshipService.getRelatedItemsByLabel(this.item, label);
- }
-
- /**
- * Get FieldUpdates for the relationships of a specific type
- * @param label The relationship type's label
- */
- public getUpdatesByLabel(label: string): Observable {
- return this.getRelatedItemsByLabel(label).pipe(
- switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, items))
- )
- }
-
- /**
- * Get the i18n message key for a relationship
- * @param label The relationship type's label
- */
- public getRelationshipMessageKey(label: string): string {
- if (label.indexOf('Of') > -1) {
- return `relationships.${label.substring(0, label.indexOf('Of') + 2)}`
- } else {
- return label;
- }
+ ngOnDestroy(): void {
+ this.itemUpdateSubscription.unsubscribe();
}
}
diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts
index d415da50b3..c1a78225b8 100644
--- a/src/app/core/cache/object-cache.service.ts
+++ b/src/app/core/cache/object-cache.service.ts
@@ -3,7 +3,7 @@ import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
import { applyPatch, Operation } from 'fast-json-patch';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
-import { distinctUntilChanged, filter, map, mergeMap, take, } from 'rxjs/operators';
+import { distinctUntilChanged, filter, map, mergeMap, take, tap, } from 'rxjs/operators';
import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util';
import { CoreState } from '../core.reducers';
import { coreSelector } from '../core.selectors';
@@ -224,6 +224,18 @@ export class ObjectCacheService {
return result;
}
+ /**
+ * Create an observable that emits a new value whenever the availability of the cached object changes.
+ * The value it emits is a boolean stating if the object exists in cache or not.
+ * @param selfLink The self link of the object to observe
+ */
+ hasBySelfLinkObservable(selfLink: string): Observable {
+ return this.store.pipe(
+ select(entryFromSelfLinkSelector(selfLink)),
+ map((entry: ObjectCacheEntry) => this.isValid(entry))
+ );
+ }
+
/**
* Check whether an ObjectCacheEntry should still be cached
*
diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts
index 82c2a47491..f15a60d5ec 100644
--- a/src/app/core/data/request.service.ts
+++ b/src/app/core/data/request.service.ts
@@ -265,4 +265,15 @@ export class RequestService {
return result;
}
+ /**
+ * Create an observable that emits a new value whenever the availability of the cached request changes.
+ * The value it emits is a boolean stating if the request exists in cache or not.
+ * @param href The href of the request to observe
+ */
+ hasByHrefObservable(href: string): Observable {
+ return this.getByHref(href).pipe(
+ map((requestEntry: RequestEntry) => this.isValid(requestEntry))
+ );
+ }
+
}