mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-12 12:33:07 +00:00
Merge branch 'w2p-64574_Item-page-entities' into w2p-62849_relationships-in-submission
This commit is contained in:
@@ -232,10 +232,10 @@
|
|||||||
"item.edit.relationships.edit.buttons.remove": "Remove",
|
"item.edit.relationships.edit.buttons.remove": "Remove",
|
||||||
"item.edit.relationships.edit.buttons.undo": "Undo changes",
|
"item.edit.relationships.edit.buttons.undo": "Undo changes",
|
||||||
"item.edit.relationships.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button",
|
"item.edit.relationships.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button",
|
||||||
"item.edit.relationships.notifications.discarded.title": "Changed discarded",
|
"item.edit.relationships.notifications.discarded.title": "Changes discarded",
|
||||||
"item.edit.relationships.notifications.failed.title": "Error deleting relationship",
|
"item.edit.relationships.notifications.failed.title": "Error deleting relationship",
|
||||||
"item.edit.relationships.notifications.outdated.content": "The item you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts",
|
"item.edit.relationships.notifications.outdated.content": "The item you're currently working on has been changed by another user. Your current changes are discarded to prevent conflicts",
|
||||||
"item.edit.relationships.notifications.outdated.title": "Changed outdated",
|
"item.edit.relationships.notifications.outdated.title": "Changes outdated",
|
||||||
"item.edit.relationships.notifications.saved.content": "Your changes to this item's relationships were saved.",
|
"item.edit.relationships.notifications.saved.content": "Your changes to this item's relationships were saved.",
|
||||||
"item.edit.relationships.notifications.saved.title": "Relationships saved",
|
"item.edit.relationships.notifications.saved.title": "Relationships saved",
|
||||||
"item.edit.relationships.reinstate-button": "Undo",
|
"item.edit.relationships.reinstate-button": "Undo",
|
||||||
@@ -294,6 +294,8 @@
|
|||||||
"item.page.link.full": "Full item page",
|
"item.page.link.full": "Full item page",
|
||||||
"item.page.link.simple": "Simple item page",
|
"item.page.link.simple": "Simple item page",
|
||||||
"item.page.person.search.title": "Articles by this author",
|
"item.page.person.search.title": "Articles by this author",
|
||||||
|
"item.page.related-items.view-more": "View more",
|
||||||
|
"item.page.related-items.view-less": "View less",
|
||||||
"item.page.subject": "Keywords",
|
"item.page.subject": "Keywords",
|
||||||
"item.page.uri": "URI",
|
"item.page.uri": "URI",
|
||||||
"item.select.confirm": "Confirm selected",
|
"item.select.confirm": "Confirm selected",
|
||||||
|
@@ -17,8 +17,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { RegistryService } from '../../../core/registry/registry.service';
|
import { RegistryService } from '../../../core/registry/registry.service';
|
||||||
import { MetadatumViewModel } from '../../../core/shared/metadata.models';
|
import { MetadatumViewModel } from '../../../core/shared/metadata.models';
|
||||||
import { Metadata } from '../../../core/shared/metadata.utils';
|
import { Metadata } from '../../../core/shared/metadata.utils';
|
||||||
import { MetadataField } from '../../../core/metadata/metadata-field.model';
|
|
||||||
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
||||||
|
import { MetadataField } from '../../../core/metadata/metadata-field.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-metadata',
|
selector: 'ds-item-metadata',
|
||||||
|
@@ -36,7 +36,6 @@ let relationshipType;
|
|||||||
describe('EditRelationshipListComponent', () => {
|
describe('EditRelationshipListComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
relationshipType = Object.assign(new RelationshipType(), {
|
relationshipType = Object.assign(new RelationshipType(), {
|
||||||
type: ResourceType.RelationshipType,
|
|
||||||
id: '1',
|
id: '1',
|
||||||
uuid: '1',
|
uuid: '1',
|
||||||
leftLabel: 'isAuthorOfPublication',
|
leftLabel: 'isAuthorOfPublication',
|
||||||
@@ -98,7 +97,7 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
|
|
||||||
relationshipService = jasmine.createSpyObj('relationshipService',
|
relationshipService = jasmine.createSpyObj('relationshipService',
|
||||||
{
|
{
|
||||||
getRelatedItemsByLabel: observableOf([author1, author2]),
|
getRelatedItemsByLabel: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [author1, author2]))),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -4,8 +4,10 @@ import { Observable } from 'rxjs/internal/Observable';
|
|||||||
import { FieldUpdate, FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer';
|
import { FieldUpdate, FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer';
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
import { hasValue } from '../../../../shared/empty.util';
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-relationship-list',
|
selector: 'ds-edit-relationship-list',
|
||||||
@@ -63,7 +65,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges {
|
|||||||
* Transform the item's relationships of a specific type into related items
|
* Transform the item's relationships of a specific type into related items
|
||||||
* @param label The relationship type's label
|
* @param label The relationship type's label
|
||||||
*/
|
*/
|
||||||
public getRelatedItemsByLabel(label: string): Observable<Item[]> {
|
public getRelatedItemsByLabel(label: string): Observable<RemoteData<PaginatedList<Item>>> {
|
||||||
return this.relationshipService.getRelatedItemsByLabel(this.item, label);
|
return this.relationshipService.getRelatedItemsByLabel(this.item, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges {
|
|||||||
*/
|
*/
|
||||||
public getUpdatesByLabel(label: string): Observable<FieldUpdates> {
|
public getUpdatesByLabel(label: string): Observable<FieldUpdates> {
|
||||||
return this.getRelatedItemsByLabel(label).pipe(
|
return this.getRelatedItemsByLabel(label).pipe(
|
||||||
switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, items))
|
switchMap((itemsRD) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, itemsRD.payload.page))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,7 +32,6 @@ let el;
|
|||||||
describe('EditRelationshipComponent', () => {
|
describe('EditRelationshipComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
relationshipType = Object.assign(new RelationshipType(), {
|
relationshipType = Object.assign(new RelationshipType(), {
|
||||||
type: ResourceType.RelationshipType,
|
|
||||||
id: '1',
|
id: '1',
|
||||||
uuid: '1',
|
uuid: '1',
|
||||||
leftLabel: 'isAuthorOfPublication',
|
leftLabel: 'isAuthorOfPublication',
|
||||||
|
@@ -66,7 +66,6 @@ describe('ItemRelationshipsComponent', () => {
|
|||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
|
||||||
relationshipType = Object.assign(new RelationshipType(), {
|
relationshipType = Object.assign(new RelationshipType(), {
|
||||||
type: ResourceType.RelationshipType,
|
|
||||||
id: '1',
|
id: '1',
|
||||||
uuid: '1',
|
uuid: '1',
|
||||||
leftLabel: 'isAuthorOfPublication',
|
leftLabel: 'isAuthorOfPublication',
|
||||||
@@ -227,10 +226,8 @@ describe('ItemRelationshipsComponent', () => {
|
|||||||
comp.submit();
|
comp.submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('it should delete the correct relationship and de-cache the current item', () => {
|
it('it should delete the correct relationship', () => {
|
||||||
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationships[1].uuid);
|
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationships[1].uuid);
|
||||||
expect(objectCache.remove).toHaveBeenCalledWith(item.self);
|
|
||||||
expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(item.self);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -140,10 +140,6 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
this.notificationsService.error(this.getNotificationTitle('failed'), response.errorMessage);
|
this.notificationsService.error(this.getNotificationTitle('failed'), response.errorMessage);
|
||||||
});
|
});
|
||||||
if (successfulResponses.length > 0) {
|
if (successfulResponses.length > 0) {
|
||||||
// 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);
|
|
||||||
// Send a notification that the removal was successful
|
|
||||||
this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved'));
|
this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,19 +28,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6">
|
<div class="col-xs-12 col-md-6">
|
||||||
<ds-metadata-representation-list
|
<ds-metadata-representation-list
|
||||||
[label]="'relationships.isAuthorOf' | translate"
|
[parentItem]="item"
|
||||||
[representations]="authors$ | async">
|
[itemType]="'Person'"
|
||||||
|
[metadataField]="'dc.contributor.author'"
|
||||||
|
[label]="'relationships.isAuthorOf' | translate">
|
||||||
</ds-metadata-representation-list>
|
</ds-metadata-representation-list>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="projects$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isProjectOfPublication'"
|
||||||
[label]="'relationships.isProjectOf' | translate">
|
[label]="'relationships.isProjectOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="orgUnits$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isOrgUnitOfPublication'"
|
||||||
[label]="'relationships.isOrgUnitOf' | translate">
|
[label]="'relationships.isOrgUnitOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="journalIssues$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isJournalIssueOfPublication'"
|
||||||
[label]="'relationships.isJournalIssueOf' | translate">
|
[label]="'relationships.isJournalIssueOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
|
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
|
||||||
|
@@ -1,12 +1,9 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_ITEM_TYPE, ItemViewMode,
|
DEFAULT_ITEM_TYPE, ItemViewMode,
|
||||||
rendersItemType
|
rendersItemType
|
||||||
} from '../../../../shared/items/item-type-decorator';
|
} from '../../../../shared/items/item-type-decorator';
|
||||||
import { ItemComponent } from '../shared/item.component';
|
import { ItemComponent } from '../shared/item.component';
|
||||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
|
||||||
|
|
||||||
@rendersItemType('Publication', ItemViewMode.Detail)
|
@rendersItemType('Publication', ItemViewMode.Detail)
|
||||||
@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Detail)
|
@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Detail)
|
||||||
@@ -16,36 +13,5 @@ import { MetadataRepresentation } from '../../../../core/shared/metadata-represe
|
|||||||
templateUrl: './publication.component.html',
|
templateUrl: './publication.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class PublicationComponent extends ItemComponent implements OnInit {
|
export class PublicationComponent extends ItemComponent {
|
||||||
/**
|
|
||||||
* The authors related to this publication
|
|
||||||
*/
|
|
||||||
authors$: Observable<MetadataRepresentation[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The projects related to this publication
|
|
||||||
*/
|
|
||||||
projects$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The organisation units related to this publication
|
|
||||||
*/
|
|
||||||
orgUnits$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The journal issues related to this publication
|
|
||||||
*/
|
|
||||||
journalIssues$: Observable<Item[]>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
super.ngOnInit();
|
|
||||||
|
|
||||||
this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author');
|
|
||||||
|
|
||||||
this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication');
|
|
||||||
|
|
||||||
this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication');
|
|
||||||
|
|
||||||
this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,12 @@
|
|||||||
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
|
||||||
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
|
||||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
|
||||||
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
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 { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
|
||||||
import { distinctUntilChanged, filter, flatMap, map, startWith, switchMap } from 'rxjs/operators';
|
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
||||||
import {
|
|
||||||
combineLatest as observableCombineLatest,
|
|
||||||
of as observableOf,
|
|
||||||
zip as observableZip
|
|
||||||
} from 'rxjs';
|
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operator for comparing arrays using a mapping function
|
* Operator for comparing arrays using a mapping function
|
||||||
@@ -28,7 +17,7 @@ import { PaginatedList } from '../../../../core/data/paginated-list';
|
|||||||
*/
|
*/
|
||||||
export const compareArraysUsing = <T>(mapFn: (t: T) => any) =>
|
export const compareArraysUsing = <T>(mapFn: (t: T) => any) =>
|
||||||
(a: T[], b: T[]): boolean => {
|
(a: T[], b: T[]): boolean => {
|
||||||
if (!Array.isArray(a) || !Array.isArray(b)) {
|
if (!Array.isArray(a) || ! Array.isArray(b)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,39 +64,35 @@ export const relationsToItems = (thisId: string) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata
|
* Operator for turning a paginated list of relationships into a paginated list of the relevant items
|
||||||
* @param parentId The id of the parent item
|
* The result is wrapped in the original RemoteData and PaginatedList
|
||||||
* @param itemType The type of relation this list resembles (for creating representations)
|
* @param {string} thisId The item's id of which the relations belong to
|
||||||
* @param metadata The list of original Metadatum objects
|
* @returns {(source: Observable<Relationship[]>) => Observable<Item[]>}
|
||||||
* @param ids The ItemDataService to use for fetching Items from the Rest API
|
|
||||||
*/
|
*/
|
||||||
export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[], ids: ItemDataService) =>
|
export const paginatedRelationsToItems = (thisId: string) =>
|
||||||
(source: Observable<Relationship[]>): Observable<MetadataRepresentation[]> =>
|
(source: Observable<RemoteData<PaginatedList<Relationship>>>): Observable<RemoteData<PaginatedList<Item>>> =>
|
||||||
source.pipe(
|
source.pipe(
|
||||||
flatMap((rels: Relationship[]) =>
|
getSucceededRemoteData(),
|
||||||
observableZip(
|
switchMap((relationshipsRD: RemoteData<PaginatedList<Relationship>>) => {
|
||||||
...metadata
|
return observableZip(
|
||||||
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
...relationshipsRD.payload.page.map((rel: Relationship) => observableCombineLatest(rel.leftItem, rel.rightItem))
|
||||||
.map((metadatum: MetadataValue) => {
|
).pipe(
|
||||||
if (metadatum.isVirtual) {
|
map((arr) =>
|
||||||
const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue);
|
arr
|
||||||
if (matchingRels.length > 0) {
|
.filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded)
|
||||||
const matchingRel = matchingRels[0];
|
.map(([leftItem, rightItem]) => {
|
||||||
return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe(
|
if (leftItem.payload.id === thisId) {
|
||||||
map(([leftItem, rightItem]) => {
|
return rightItem.payload;
|
||||||
if (leftItem.payload.id === parentId) {
|
} else if (rightItem.payload.id === thisId) {
|
||||||
return rightItem.payload;
|
return leftItem.payload;
|
||||||
} else if (rightItem.payload.id === parentId) {
|
|
||||||
return leftItem.payload;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
return observableOf(Object.assign(new MetadatumRepresentation(itemType), metadatum));
|
.filter((item: Item) => hasValue(item))
|
||||||
}
|
),
|
||||||
})
|
distinctUntilChanged(compareArraysUsingIds()),
|
||||||
|
map((relatedItems: Item[]) =>
|
||||||
|
Object.assign(relationshipsRD, { payload: Object.assign(relationshipsRD.payload, { page: relatedItems } )})
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
})
|
||||||
);
|
);
|
@@ -12,18 +12,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
|||||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||||
import { ItemComponent } from './item.component';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { VarDirective } from '../../../../shared/utils/var.directive';
|
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
|
||||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
|
||||||
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
|
||||||
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
|
||||||
import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
|
|
||||||
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
@@ -311,116 +301,4 @@ describe('ItemComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when calling buildRepresentations', () => {
|
|
||||||
let comp: ItemComponent;
|
|
||||||
let fixture: ComponentFixture<ItemComponent>;
|
|
||||||
|
|
||||||
const metadataField = 'dc.contributor.author';
|
|
||||||
const relatedItem = Object.assign(new Item(), {
|
|
||||||
id: '2',
|
|
||||||
metadata: Object.assign(new MetadataMap(), {
|
|
||||||
'dc.title': [
|
|
||||||
{
|
|
||||||
language: 'en_US',
|
|
||||||
value: 'related item'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
const mockItem = Object.assign(new Item(), {
|
|
||||||
id: '1',
|
|
||||||
uuid: '1',
|
|
||||||
metadata: new MetadataMap()
|
|
||||||
});
|
|
||||||
mockItem.relationships = createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [
|
|
||||||
Object.assign(new Relationship(), {
|
|
||||||
uuid: '123',
|
|
||||||
id: '123',
|
|
||||||
leftItem: createSuccessfulRemoteDataObject$(mockItem),
|
|
||||||
rightItem: createSuccessfulRemoteDataObject$(relatedItem),
|
|
||||||
relationshipType: createSuccessfulRemoteDataObject$(new RelationshipType())
|
|
||||||
})
|
|
||||||
]));
|
|
||||||
mockItem.metadata[metadataField] = [
|
|
||||||
{
|
|
||||||
value: 'Second value',
|
|
||||||
place: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Third value',
|
|
||||||
place: 2,
|
|
||||||
authority: 'virtual::123'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'First value',
|
|
||||||
place: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Fourth value',
|
|
||||||
place: 3,
|
|
||||||
authority: '123'
|
|
||||||
}
|
|
||||||
] as MetadataValue[];
|
|
||||||
const mockItemDataService = Object.assign({
|
|
||||||
findById: (id) => {
|
|
||||||
if (id === relatedItem.id) {
|
|
||||||
return createSuccessfulRemoteDataObject$(relatedItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) as ItemDataService;
|
|
||||||
|
|
||||||
let representations: Observable<MetadataRepresentation[]>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [TranslateModule.forRoot({
|
|
||||||
loader: {
|
|
||||||
provide: TranslateLoader,
|
|
||||||
useClass: MockTranslateLoader
|
|
||||||
}
|
|
||||||
}), BrowserAnimationsModule],
|
|
||||||
declarations: [ItemComponent, VarDirective],
|
|
||||||
providers: [
|
|
||||||
{provide: ITEM, useValue: mockItem},
|
|
||||||
{provide: RelationshipService, useValue: {}}
|
|
||||||
],
|
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
|
||||||
}).overrideComponent(ItemComponent, {
|
|
||||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
fixture = TestBed.createComponent(ItemComponent);
|
|
||||||
comp = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
representations = comp.buildRepresentations('bogus', metadataField);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should contain exactly 4 metadata-representations', () => {
|
|
||||||
representations.subscribe((reps: MetadataRepresentation[]) => {
|
|
||||||
expect(reps.length).toEqual(4);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have all the representations in the correct order', () => {
|
|
||||||
representations.subscribe((reps: MetadataRepresentation[]) => {
|
|
||||||
expect(reps[0].getValue()).toEqual('First value');
|
|
||||||
expect(reps[1].getValue()).toEqual('Second value');
|
|
||||||
expect(reps[2].getValue()).toEqual('related item');
|
|
||||||
expect(reps[3].getValue()).toEqual('Fourth value');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have created the correct MetadatumRepresentation and ItemMetadataRepresentation objects for the correct Metadata', () => {
|
|
||||||
representations.subscribe((reps: MetadataRepresentation[]) => {
|
|
||||||
expect(reps[0] instanceof MetadatumRepresentation).toEqual(true);
|
|
||||||
expect(reps[1] instanceof MetadatumRepresentation).toEqual(true);
|
|
||||||
expect(reps[2] instanceof ItemMetadataRepresentation).toEqual(true);
|
|
||||||
expect(reps[3] instanceof MetadatumRepresentation).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,59 +1,6 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
|
|
||||||
import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators';
|
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
|
||||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
|
||||||
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
|
||||||
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
|
||||||
import { of } from 'rxjs/internal/observable/of';
|
|
||||||
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
|
||||||
import { compareArraysUsingIds } from './item-relationships-utils';
|
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata
|
|
||||||
* @param thisId The id of the parent item
|
|
||||||
* @param itemType The type of relation this list resembles (for creating representations)
|
|
||||||
* @param metadata The list of original Metadatum objects
|
|
||||||
*/
|
|
||||||
export const relationsToRepresentations = (thisId: string, itemType: string, metadata: MetadataValue[]) =>
|
|
||||||
(source: Observable<Relationship[]>): Observable<MetadataRepresentation[]> =>
|
|
||||||
source.pipe(
|
|
||||||
flatMap((rels: Relationship[]) =>
|
|
||||||
observableZip(
|
|
||||||
...metadata
|
|
||||||
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
|
||||||
.map((metadatum: MetadataValue) => {
|
|
||||||
if (metadatum.isVirtual) {
|
|
||||||
const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue);
|
|
||||||
if (matchingRels.length > 0) {
|
|
||||||
const matchingRel = matchingRels[0];
|
|
||||||
return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe(
|
|
||||||
filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded),
|
|
||||||
map(([leftItem, rightItem]) => {
|
|
||||||
if (leftItem.payload.id === thisId) {
|
|
||||||
return rightItem.payload;
|
|
||||||
} else if (rightItem.payload.id === thisId) {
|
|
||||||
return leftItem.payload;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return of(Object.assign(new MetadatumRepresentation(itemType), metadatum));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item',
|
selector: 'ds-item',
|
||||||
@@ -62,33 +9,9 @@ export const relationsToRepresentations = (thisId: string, itemType: string, met
|
|||||||
/**
|
/**
|
||||||
* A generic component for displaying metadata and relations of an item
|
* A generic component for displaying metadata and relations of an item
|
||||||
*/
|
*/
|
||||||
export class ItemComponent implements OnInit {
|
export class ItemComponent {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(ITEM) public item: Item,
|
@Inject(ITEM) public item: Item
|
||||||
protected relationshipService: RelationshipService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a list of MetadataRepresentations for the current item. This combines all metadata and relationships of a
|
|
||||||
* certain type.
|
|
||||||
* @param itemType The type of item we're building representations of. Used for matching templates.
|
|
||||||
* @param metadataField The metadata field that resembles the item type.
|
|
||||||
*/
|
|
||||||
buildRepresentations(itemType: string, metadataField: string): Observable<MetadataRepresentation[]> {
|
|
||||||
const metadata = this.item.findMetadataSortedByPlace(metadataField);
|
|
||||||
const relsCurrentPage$ = this.item.relationships.pipe(
|
|
||||||
getSucceededRemoteData(),
|
|
||||||
getRemoteDataPayload(),
|
|
||||||
map((pl: PaginatedList<Relationship>) => pl.page),
|
|
||||||
distinctUntilChanged(compareArraysUsingIds())
|
|
||||||
);
|
|
||||||
|
|
||||||
return relsCurrentPage$.pipe(
|
|
||||||
relationsToRepresentations(this.item.id, itemType, metadata)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,11 @@
|
|||||||
<ds-metadata-field-wrapper *ngIf="representations && representations.length > 0" [label]="label">
|
<ds-metadata-field-wrapper *ngIf="representations$ && (representations$ | async)?.length > 0" [label]="label">
|
||||||
<ds-item-type-switcher *ngFor="let rep of representations"
|
<ds-item-type-switcher *ngFor="let rep of (representations$ | async)"
|
||||||
[object]="rep" [viewMode]="viewMode">
|
[object]="rep" [viewMode]="viewMode">
|
||||||
</ds-item-type-switcher>
|
</ds-item-type-switcher>
|
||||||
|
<div *ngIf="(representations$ | async)?.length < total" class="mt-2">
|
||||||
|
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="limit > originalLimit" class="mt-2">
|
||||||
|
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
||||||
|
</div>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -2,23 +2,72 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { MetadataRepresentationListComponent } from './metadata-representation-list.component';
|
import { MetadataRepresentationListComponent } from './metadata-representation-list.component';
|
||||||
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||||
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
const itemType = 'type';
|
const itemType = 'Person';
|
||||||
const metadataRepresentation1 = new MetadatumRepresentation(itemType);
|
const metadataField = 'dc.contributor.author';
|
||||||
const metadataRepresentation2 = new ItemMetadataRepresentation();
|
const parentItem: Item = Object.assign(new Item(), {
|
||||||
const representations = [metadataRepresentation1, metadataRepresentation2];
|
id: 'parent-item',
|
||||||
|
metadata: {
|
||||||
|
'dc.contributor.author': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'Related Author with authority',
|
||||||
|
authority: 'virtual::related-author',
|
||||||
|
place: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'Author without authority',
|
||||||
|
place: 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'dc.title': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'Parent Item'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const relatedAuthor: Item = Object.assign(new Item(), {
|
||||||
|
id: 'related-author',
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'Related Author'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const relation: Relationship = Object.assign(new Relationship(), {
|
||||||
|
leftItem: createSuccessfulRemoteDataObject$(parentItem),
|
||||||
|
rightItem: createSuccessfulRemoteDataObject$(relatedAuthor)
|
||||||
|
});
|
||||||
|
let relationshipService: RelationshipService;
|
||||||
|
|
||||||
describe('MetadataRepresentationListComponent', () => {
|
describe('MetadataRepresentationListComponent', () => {
|
||||||
let comp: MetadataRepresentationListComponent;
|
let comp: MetadataRepresentationListComponent;
|
||||||
let fixture: ComponentFixture<MetadataRepresentationListComponent>;
|
let fixture: ComponentFixture<MetadataRepresentationListComponent>;
|
||||||
|
|
||||||
|
relationshipService = jasmine.createSpyObj('relationshipService',
|
||||||
|
{
|
||||||
|
findById: createSuccessfulRemoteDataObject$(relation)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [],
|
imports: [TranslateModule.forRoot()],
|
||||||
declarations: [MetadataRepresentationListComponent],
|
declarations: [MetadataRepresentationListComponent],
|
||||||
providers: [],
|
providers: [
|
||||||
|
{ provide: RelationshipService, useValue: relationshipService }
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(MetadataRepresentationListComponent, {
|
}).overrideComponent(MetadataRepresentationListComponent, {
|
||||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
set: {changeDetection: ChangeDetectionStrategy.Default}
|
||||||
@@ -28,13 +77,45 @@ describe('MetadataRepresentationListComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
fixture = TestBed.createComponent(MetadataRepresentationListComponent);
|
fixture = TestBed.createComponent(MetadataRepresentationListComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
comp.representations = representations;
|
comp.parentItem = parentItem;
|
||||||
|
comp.itemType = itemType;
|
||||||
|
comp.metadataField = metadataField;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it(`should load ${representations.length} item-type-switcher components`, () => {
|
it('should load 2 item-type-switcher components', () => {
|
||||||
const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
|
const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
|
||||||
expect(fields.length).toBe(representations.length);
|
expect(fields.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize the original limit', () => {
|
||||||
|
expect(comp.originalLimit).toEqual(comp.limit);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when viewMore is called', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.viewMore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the limit to a high number in order to retrieve all metadata representations', () => {
|
||||||
|
expect(comp.limit).toBeGreaterThanOrEqual(999);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when viewLess is called', () => {
|
||||||
|
let originalLimit;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Store the original value of limit
|
||||||
|
originalLimit = comp.limit;
|
||||||
|
// Set limit to a random number
|
||||||
|
comp.limit = 458;
|
||||||
|
comp.viewLess();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset the limit to the original value', () => {
|
||||||
|
expect(comp.limit).toEqual(originalLimit);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,17 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
||||||
import { ItemViewMode } from '../../../shared/items/item-type-decorator';
|
import { ItemViewMode } from '../../../shared/items/item-type-decorator';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { zip as observableZip, combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
|
||||||
|
import { MetadataValue } from '../../../core/shared/metadata.models';
|
||||||
|
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||||
|
import { filter, map, switchMap } from 'rxjs/operators';
|
||||||
|
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
|
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||||
|
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-metadata-representation-list',
|
selector: 'ds-metadata-representation-list',
|
||||||
@@ -8,22 +19,123 @@ import { ItemViewMode } from '../../../shared/items/item-type-decorator';
|
|||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* This component is used for displaying metadata
|
* This component is used for displaying metadata
|
||||||
* It expects a list of MetadataRepresentation objects and a label to put on top of the list
|
* It expects an item and a metadataField to fetch metadata
|
||||||
|
* It expects an itemType to resolve the metadata to a an item
|
||||||
|
* It expects a label to put on top of the list
|
||||||
*/
|
*/
|
||||||
export class MetadataRepresentationListComponent {
|
export class MetadataRepresentationListComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* A list of metadata-representations to display
|
* The parent of the list of related items to display
|
||||||
*/
|
*/
|
||||||
@Input() representations: MetadataRepresentation[];
|
@Input() parentItem: Item;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of item to create a representation of
|
||||||
|
*/
|
||||||
|
@Input() itemType: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The metadata field to use for fetching metadata from the item
|
||||||
|
*/
|
||||||
|
@Input() metadataField: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An i18n label to use as a title for the list
|
* An i18n label to use as a title for the list
|
||||||
*/
|
*/
|
||||||
@Input() label: string;
|
@Input() label: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The max amount of representations to display
|
||||||
|
* Defaults to 10
|
||||||
|
* The default can optionally be overridden by providing the limit as input to the component
|
||||||
|
*/
|
||||||
|
@Input() limit = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of metadata-representations to display
|
||||||
|
*/
|
||||||
|
representations$: Observable<MetadataRepresentation[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The view-mode we're currently on
|
* The view-mode we're currently on
|
||||||
* @type {ElementViewMode}
|
* @type {ElementViewMode}
|
||||||
*/
|
*/
|
||||||
viewMode = ItemViewMode.Metadata;
|
viewMode = ItemViewMode.Metadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The originally provided limit
|
||||||
|
* Used for resetting the limit to the original value when collapsing the list
|
||||||
|
*/
|
||||||
|
originalLimit: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total amount of metadata values available
|
||||||
|
*/
|
||||||
|
total: number;
|
||||||
|
|
||||||
|
constructor(public relationshipService: RelationshipService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.originalLimit = this.limit;
|
||||||
|
this.setRepresentations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the metadata representations
|
||||||
|
*/
|
||||||
|
setRepresentations() {
|
||||||
|
const metadata = this.parentItem.findMetadataSortedByPlace(this.metadataField);
|
||||||
|
this.total = metadata.length;
|
||||||
|
this.representations$ = this.resolveMetadataRepresentations(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a list of metadata values to a list of metadata representations
|
||||||
|
* @param metadata
|
||||||
|
*/
|
||||||
|
resolveMetadataRepresentations(metadata: MetadataValue[]): Observable<MetadataRepresentation[]> {
|
||||||
|
return observableZip(
|
||||||
|
...metadata
|
||||||
|
.slice(0, this.limit)
|
||||||
|
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
||||||
|
.map((metadatum: MetadataValue) => {
|
||||||
|
if (metadatum.isVirtual) {
|
||||||
|
return this.relationshipService.findById(metadatum.virtualValue).pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
switchMap((relRD: RemoteData<Relationship>) =>
|
||||||
|
observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe(
|
||||||
|
filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded),
|
||||||
|
map(([leftItem, rightItem]) => {
|
||||||
|
if (leftItem.payload.id === this.parentItem.id) {
|
||||||
|
return rightItem.payload;
|
||||||
|
} else if (rightItem.payload.id === this.parentItem.id) {
|
||||||
|
return leftItem.payload;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item))
|
||||||
|
)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return observableOf(Object.assign(new MetadatumRepresentation(this.itemType), metadatum));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the list to display all metadata representations
|
||||||
|
*/
|
||||||
|
viewMore() {
|
||||||
|
this.limit = 9999;
|
||||||
|
this.setRepresentations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse the list to display the originally displayed metadata representations
|
||||||
|
*/
|
||||||
|
viewLess() {
|
||||||
|
this.limit = this.originalLimit;
|
||||||
|
this.setRepresentations();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { ItemViewMode } from '../../../shared/items/item-type-decorator';
|
import { ItemViewMode } from '../../../shared/items/item-type-decorator';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||||
|
import { FindAllOptions } from '../../../core/data/request.models';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-related-items',
|
selector: 'ds-related-items',
|
||||||
@@ -9,22 +14,72 @@ import { ItemViewMode } from '../../../shared/items/item-type-decorator';
|
|||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* This component is used for displaying relations between items
|
* This component is used for displaying relations between items
|
||||||
* It expects a list of items to display and a label to put on top
|
* It expects a parent item and relationship type, as well as a label to display on top
|
||||||
*/
|
*/
|
||||||
export class RelatedItemsComponent {
|
export class RelatedItemsComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* A list of items to display
|
* The parent of the list of related items to display
|
||||||
*/
|
*/
|
||||||
@Input() items: Item[];
|
@Input() parentItem: Item;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The label of the relationship type to display
|
||||||
|
* Used in sending a search request to the REST API
|
||||||
|
*/
|
||||||
|
@Input() relationType: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default options to start a search request with
|
||||||
|
* Optional input, should you wish a different page size (or other options)
|
||||||
|
*/
|
||||||
|
@Input() options = Object.assign(new FindAllOptions(), { elementsPerPage: 5 });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An i18n label to use as a title for the list (usually describes the relation)
|
* An i18n label to use as a title for the list (usually describes the relation)
|
||||||
*/
|
*/
|
||||||
@Input() label: string;
|
@Input() label: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of related items
|
||||||
|
*/
|
||||||
|
items$: Observable<RemoteData<PaginatedList<Item>>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search options for displaying all elements in a list
|
||||||
|
*/
|
||||||
|
allOptions = Object.assign(new FindAllOptions(), { elementsPerPage: 9999 });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The view-mode we're currently on
|
* The view-mode we're currently on
|
||||||
* @type {ElementViewMode}
|
* @type {ElementViewMode}
|
||||||
*/
|
*/
|
||||||
viewMode = ItemViewMode.Summary;
|
viewMode = ItemViewMode.Summary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the list is currently expanded to show all related items
|
||||||
|
*/
|
||||||
|
showingAll = false;
|
||||||
|
|
||||||
|
constructor(public relationshipService: RelationshipService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the list to display all related items
|
||||||
|
*/
|
||||||
|
viewMore() {
|
||||||
|
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.allOptions);
|
||||||
|
this.showingAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse the list to display the originally displayed items
|
||||||
|
*/
|
||||||
|
viewLess() {
|
||||||
|
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options);
|
||||||
|
this.showingAll = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,11 @@
|
|||||||
<ds-metadata-field-wrapper *ngIf="items && items.length > 0" [label]="label">
|
<ds-metadata-field-wrapper *ngIf="(items$ | async)?.payload?.page?.length > 0" [label]="label">
|
||||||
<ds-item-type-switcher *ngFor="let item of items"
|
<ds-item-type-switcher *ngFor="let item of (items$ | async)?.payload?.page"
|
||||||
[object]="item" [viewMode]="viewMode">
|
[object]="item" [viewMode]="viewMode">
|
||||||
</ds-item-type-switcher>
|
</ds-item-type-switcher>
|
||||||
|
<div *ngIf="(items$ | async)?.payload?.page?.length < (items$ | async)?.payload?.totalElements" class="mt-2" id="view-more">
|
||||||
|
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="showingAll" class="mt-2" id="view-less">
|
||||||
|
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
||||||
|
</div>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -2,14 +2,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { RelatedItemsComponent } from './related-items-component';
|
import { RelatedItemsComponent } from './related-items-component';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { createRelationshipsObservable } from '../item-types/shared/item.component.spec';
|
import { createRelationshipsObservable } from '../item-types/shared/item.component.spec';
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
|
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
const parentItem: Item = Object.assign(new Item(), {
|
||||||
|
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
metadata: [],
|
||||||
|
relationships: createRelationshipsObservable()
|
||||||
|
});
|
||||||
const mockItem1: Item = Object.assign(new Item(), {
|
const mockItem1: Item = Object.assign(new Item(), {
|
||||||
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
metadata: [],
|
metadata: [],
|
||||||
@@ -21,16 +26,26 @@ const mockItem2: Item = Object.assign(new Item(), {
|
|||||||
relationships: createRelationshipsObservable()
|
relationships: createRelationshipsObservable()
|
||||||
});
|
});
|
||||||
const mockItems = [mockItem1, mockItem2];
|
const mockItems = [mockItem1, mockItem2];
|
||||||
|
const relationType = 'isItemOfItem';
|
||||||
|
let relationshipService: RelationshipService;
|
||||||
|
|
||||||
describe('RelatedItemsComponent', () => {
|
describe('RelatedItemsComponent', () => {
|
||||||
let comp: RelatedItemsComponent;
|
let comp: RelatedItemsComponent;
|
||||||
let fixture: ComponentFixture<RelatedItemsComponent>;
|
let fixture: ComponentFixture<RelatedItemsComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
|
relationshipService = jasmine.createSpyObj('relationshipService',
|
||||||
|
{
|
||||||
|
getRelatedItemsByLabel: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockItems)),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [],
|
imports: [TranslateModule.forRoot()],
|
||||||
declarations: [RelatedItemsComponent],
|
declarations: [RelatedItemsComponent],
|
||||||
providers: [],
|
providers: [
|
||||||
|
{ provide: RelationshipService, useValue: relationshipService }
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(RelatedItemsComponent, {
|
}).overrideComponent(RelatedItemsComponent, {
|
||||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
set: {changeDetection: ChangeDetectionStrategy.Default}
|
||||||
@@ -40,7 +55,8 @@ describe('RelatedItemsComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
fixture = TestBed.createComponent(RelatedItemsComponent);
|
fixture = TestBed.createComponent(RelatedItemsComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
comp.items = mockItems;
|
comp.parentItem = parentItem;
|
||||||
|
comp.relationType = relationType;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -49,4 +65,32 @@ describe('RelatedItemsComponent', () => {
|
|||||||
expect(fields.length).toBe(mockItems.length);
|
expect(fields.length).toBe(mockItems.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when viewMore is called', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.viewMore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => {
|
||||||
|
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.allOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set showingAll to true', () => {
|
||||||
|
expect(comp.showingAll).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when viewLess is called', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.viewLess();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => {
|
||||||
|
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.options);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set showingAll to false', () => {
|
||||||
|
expect(comp.showingAll).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -112,7 +112,7 @@ export class SearchPageComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.searchOptions$ = this.getSearchOptions();
|
this.searchOptions$ = this.getSearchOptions();
|
||||||
this.sub = this.searchOptions$.pipe(
|
this.sub = this.searchOptions$.pipe(
|
||||||
switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData(), startWith(observableOf(undefined)))))
|
switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData(), startWith(undefined))))
|
||||||
.subscribe((results) => {
|
.subscribe((results) => {
|
||||||
this.resultsRD$.next(results);
|
this.resultsRD$.next(results);
|
||||||
});
|
});
|
||||||
|
@@ -13,6 +13,8 @@ import { Item } from '../shared/item.model';
|
|||||||
import { PaginatedList } from './paginated-list';
|
import { PaginatedList } from './paginated-list';
|
||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
import { DeleteRequest } from './request.models';
|
import { DeleteRequest } from './request.models';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
|
||||||
describe('RelationshipService', () => {
|
describe('RelationshipService', () => {
|
||||||
let service: RelationshipService;
|
let service: RelationshipService;
|
||||||
@@ -22,33 +24,33 @@ describe('RelationshipService', () => {
|
|||||||
const relationshipsEndpointURL = `${restEndpointURL}/relationships`;
|
const relationshipsEndpointURL = `${restEndpointURL}/relationships`;
|
||||||
const halService: any = new HALEndpointServiceStub(restEndpointURL);
|
const halService: any = new HALEndpointServiceStub(restEndpointURL);
|
||||||
const rdbService = getMockRemoteDataBuildService();
|
const rdbService = getMockRemoteDataBuildService();
|
||||||
|
const objectCache = Object.assign({
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
remove: () => {}
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
}) as ObjectCacheService;
|
||||||
|
|
||||||
const relationshipType = Object.assign(new RelationshipType(), {
|
const relationshipType = Object.assign(new RelationshipType(), {
|
||||||
type: ResourceType.RelationshipType,
|
|
||||||
id: '1',
|
id: '1',
|
||||||
uuid: '1',
|
uuid: '1',
|
||||||
leftLabel: 'isAuthorOfPublication',
|
leftLabel: 'isAuthorOfPublication',
|
||||||
rightLabel: 'isPublicationOfAuthor'
|
rightLabel: 'isPublicationOfAuthor'
|
||||||
});
|
});
|
||||||
|
|
||||||
const relationships = [
|
const relationship1 = Object.assign(new Relationship(), {
|
||||||
Object.assign(new Relationship(), {
|
self: relationshipsEndpointURL + '/2',
|
||||||
self: relationshipsEndpointURL + '/2',
|
id: '2',
|
||||||
id: '2',
|
uuid: '2',
|
||||||
uuid: '2',
|
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
||||||
leftId: 'author1',
|
});
|
||||||
rightId: 'publication',
|
const relationship2 = Object.assign(new Relationship(), {
|
||||||
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
self: relationshipsEndpointURL + '/3',
|
||||||
}),
|
id: '3',
|
||||||
Object.assign(new Relationship(), {
|
uuid: '3',
|
||||||
self: relationshipsEndpointURL + '/3',
|
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
||||||
id: '3',
|
});
|
||||||
uuid: '3',
|
|
||||||
leftId: 'author2',
|
const relationships = [ relationship1, relationship2 ];
|
||||||
rightId: 'publication',
|
|
||||||
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
const item = Object.assign(new Item(), {
|
const item = Object.assign(new Item(), {
|
||||||
self: 'fake-item-url/publication',
|
self: 'fake-item-url/publication',
|
||||||
@@ -65,6 +67,10 @@ describe('RelationshipService', () => {
|
|||||||
id: 'author2',
|
id: 'author2',
|
||||||
uuid: 'author2'
|
uuid: 'author2'
|
||||||
});
|
});
|
||||||
|
relationship1.leftItem = getRemotedataObservable(relatedItem1);
|
||||||
|
relationship1.rightItem = getRemotedataObservable(item);
|
||||||
|
relationship2.leftItem = getRemotedataObservable(relatedItem2);
|
||||||
|
relationship2.rightItem = getRemotedataObservable(item);
|
||||||
const relatedItems = [relatedItem1, relatedItem2];
|
const relatedItems = [relatedItem1, relatedItem2];
|
||||||
|
|
||||||
const itemService = jasmine.createSpyObj('itemService', {
|
const itemService = jasmine.createSpyObj('itemService', {
|
||||||
@@ -73,10 +79,16 @@ describe('RelationshipService', () => {
|
|||||||
|
|
||||||
function initTestService() {
|
function initTestService() {
|
||||||
return new RelationshipService(
|
return new RelationshipService(
|
||||||
|
itemService,
|
||||||
requestService,
|
requestService,
|
||||||
halService,
|
|
||||||
rdbService,
|
rdbService,
|
||||||
itemService
|
null,
|
||||||
|
null,
|
||||||
|
halService,
|
||||||
|
objectCache,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,13 +105,22 @@ describe('RelationshipService', () => {
|
|||||||
|
|
||||||
describe('deleteRelationship', () => {
|
describe('deleteRelationship', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
spyOn(service, 'findById').and.returnValue(getRemotedataObservable(relationship1));
|
||||||
|
spyOn(objectCache, 'remove');
|
||||||
service.deleteRelationship(relationships[0].uuid).subscribe();
|
service.deleteRelationship(relationships[0].uuid).subscribe();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should send a DeleteRequest', () => {
|
it('should send a DeleteRequest', () => {
|
||||||
const expected = new DeleteRequest(requestService.generateRequestId(), relationshipsEndpointURL + '/' + relationships[0].uuid);
|
const expected = new DeleteRequest(requestService.generateRequestId(), relationshipsEndpointURL + '/' + relationship1.uuid);
|
||||||
expect(requestService.configure).toHaveBeenCalledWith(expected, undefined);
|
expect(requestService.configure).toHaveBeenCalledWith(expected, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should clear the related items their cache', () => {
|
||||||
|
expect(objectCache.remove).toHaveBeenCalledWith(relatedItem1.self);
|
||||||
|
expect(objectCache.remove).toHaveBeenCalledWith(item.self);
|
||||||
|
expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(relatedItem1.self);
|
||||||
|
expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(item.self);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getItemRelationshipsArray', () => {
|
describe('getItemRelationshipsArray', () => {
|
||||||
@@ -126,12 +147,8 @@ describe('RelationshipService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getRelatedItemsByLabel', () => {
|
|
||||||
it('should return the related items by label', () => {
|
|
||||||
service.getRelatedItemsByLabel(item, relationshipType.rightLabel).subscribe((result) => {
|
|
||||||
expect(result).toEqual(relatedItems);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getRemotedataObservable(obj: any): Observable<RemoteData<any>> {
|
||||||
|
return observableOf(new RemoteData(false, false, true, undefined, obj));
|
||||||
|
}
|
||||||
|
@@ -2,8 +2,8 @@ import { Injectable } from '@angular/core';
|
|||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { hasNoValue, hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, hasValueOperator, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
import { distinctUntilChanged, filter, map, mergeMap, skip, startWith, switchMap, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
||||||
import { DeleteRequest, FindAllOptions, PostRequest, RestRequest } from './request.models';
|
import { DeleteRequest, FindAllOptions, PostRequest, RestRequest } from './request.models';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
@@ -15,16 +15,17 @@ import { RemoteData } from './remote-data';
|
|||||||
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { PaginatedList } from './paginated-list';
|
import { PaginatedList } from './paginated-list';
|
||||||
import { ItemDataService } from './item-data.service';
|
import { ItemDataService } from './item-data.service';
|
||||||
import { compareArraysUsingIds, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
|
||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { DataService } from './data.service';
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { DataService } from './data.service';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||||
|
import { SearchParam } from '../cache/models/search-param.model';
|
||||||
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service handling all relationship requests
|
* The service handling all relationship requests
|
||||||
@@ -61,6 +62,15 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a relationship by its UUID
|
||||||
|
* @param uuid
|
||||||
|
*/
|
||||||
|
findById(uuid: string): Observable<RemoteData<Relationship>> {
|
||||||
|
const href$ = this.getRelationshipEndpoint(uuid);
|
||||||
|
return this.rdbService.buildSingle<Relationship>(href$);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a delete request for a relationship by ID
|
* Send a delete request for a relationship by ID
|
||||||
* @param id
|
* @param id
|
||||||
@@ -183,41 +193,31 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
* and return the items as an array
|
* and return the items as an array
|
||||||
* @param item
|
* @param item
|
||||||
* @param label
|
* @param label
|
||||||
|
* @param options
|
||||||
*/
|
*/
|
||||||
getRelatedItemsByLabel(item: Item, label: string): Observable<Item[]> {
|
getRelatedItemsByLabel(item: Item, label: string, options?: FindAllOptions): Observable<RemoteData<PaginatedList<Item>>> {
|
||||||
return this.getItemRelationshipsByLabel(item, label).pipe(relationsToItems(item.uuid));
|
return this.getItemRelationshipsByLabel(item, label, options).pipe(paginatedRelationsToItems(item.uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a given item's relationships into related items, filtered by a relationship label
|
* Resolve a given item's relationships into related items, filtered by a relationship label
|
||||||
* and return the items as an array
|
* and return the items as an array
|
||||||
* @param item
|
* @param item
|
||||||
* @param label
|
* @param label
|
||||||
|
* @param options
|
||||||
*/
|
*/
|
||||||
getItemRelationshipsByLabel(item: Item, label: string): Observable<Relationship[]> {
|
getItemRelationshipsByLabel(item: Item, label: string, options?: FindAllOptions): Observable<RemoteData<PaginatedList<Relationship>>> {
|
||||||
return this.getItemRelationshipsArray(item).pipe(
|
let findAllOptions = new FindAllOptions();
|
||||||
switchMap((relationships: Relationship[]) => {
|
if (options) {
|
||||||
return observableCombineLatest(
|
findAllOptions = Object.assign(new FindAllOptions(), options);
|
||||||
...relationships.map((relationship: Relationship) => {
|
}
|
||||||
return relationship.relationshipType.pipe(
|
const searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ];
|
||||||
getSucceededRemoteData(),
|
if (findAllOptions.searchParams) {
|
||||||
getRemoteDataPayload(),
|
findAllOptions.searchParams = [...findAllOptions.searchParams, ...searchParams];
|
||||||
map((relationshipType: RelationshipType) => {
|
} else {
|
||||||
if (label === relationshipType.rightLabel || label === relationshipType.leftLabel) {
|
findAllOptions.searchParams = searchParams;
|
||||||
return relationship;
|
}
|
||||||
} else {
|
return this.searchBy('byLabel', findAllOptions);
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
map((relationships: Relationship[]) =>
|
|
||||||
relationships.filter((relationship: Relationship) => hasValue(relationship))
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -90,7 +90,7 @@ export const toDSpaceObjectListRD = () =>
|
|||||||
source.pipe(
|
source.pipe(
|
||||||
filter((rd: RemoteData<PaginatedList<SearchResult<T>>>) => rd.hasSucceeded),
|
filter((rd: RemoteData<PaginatedList<SearchResult<T>>>) => rd.hasSucceeded),
|
||||||
map((rd: RemoteData<PaginatedList<SearchResult<T>>>) => {
|
map((rd: RemoteData<PaginatedList<SearchResult<T>>>) => {
|
||||||
const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult<T>) => searchResult.indexableObject);
|
const dsoPage: T[] = rd.payload.page.filter((result) => hasValue(result)).map((searchResult: SearchResult<T>) => searchResult.indexableObject);
|
||||||
const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList<T>;
|
const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList<T>;
|
||||||
return Object.assign(rd, { payload: payload });
|
return Object.assign(rd, { payload: payload });
|
||||||
})
|
})
|
||||||
|
@@ -1,13 +1,8 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
||||||
import { Injectable, OnDestroy } from '@angular/core';
|
import { Injectable, OnDestroy } from '@angular/core';
|
||||||
import { NavigationExtras, Router } from '@angular/router';
|
import { NavigationExtras, Router } from '@angular/router';
|
||||||
import { first, map, switchMap } from 'rxjs/operators';
|
import { first, map, switchMap, tap } from 'rxjs/operators';
|
||||||
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
import { FacetConfigSuccessResponse, FacetValueSuccessResponse, SearchSuccessResponse } from '../../cache/response.models';
|
||||||
import {
|
|
||||||
FacetConfigSuccessResponse,
|
|
||||||
FacetValueSuccessResponse,
|
|
||||||
SearchSuccessResponse
|
|
||||||
} from '../../cache/response.models';
|
|
||||||
import { PaginatedList } from '../../data/paginated-list';
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
import { ResponseParsingService } from '../../data/parsing.service';
|
import { ResponseParsingService } from '../../data/parsing.service';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
@@ -16,12 +11,6 @@ import { RequestService } from '../../data/request.service';
|
|||||||
import { DSpaceObject } from '../dspace-object.model';
|
import { DSpaceObject } from '../dspace-object.model';
|
||||||
import { GenericConstructor } from '../generic-constructor';
|
import { GenericConstructor } from '../generic-constructor';
|
||||||
import { HALEndpointService } from '../hal-endpoint.service';
|
import { HALEndpointService } from '../hal-endpoint.service';
|
||||||
import {
|
|
||||||
configureRequest,
|
|
||||||
filterSuccessfulResponses,
|
|
||||||
getResponseFromEntry,
|
|
||||||
getSucceededRemoteData
|
|
||||||
} from '../operators';
|
|
||||||
import { URLCombiner } from '../../url-combiner/url-combiner';
|
import { URLCombiner } from '../../url-combiner/url-combiner';
|
||||||
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../../shared/empty.util';
|
||||||
import { NormalizedSearchResult } from '../../../shared/search/normalized-search-result.model';
|
import { NormalizedSearchResult } from '../../../shared/search/normalized-search-result.model';
|
||||||
@@ -42,6 +31,8 @@ import { CommunityDataService } from '../../data/community-data.service';
|
|||||||
import { ViewMode } from '../view-mode.model';
|
import { ViewMode } from '../view-mode.model';
|
||||||
import { DSpaceObjectDataService } from '../../data/dspace-object-data.service';
|
import { DSpaceObjectDataService } from '../../data/dspace-object-data.service';
|
||||||
import { RouteService } from '../../../shared/services/route.service';
|
import { RouteService } from '../../../shared/services/route.service';
|
||||||
|
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
||||||
|
import { configureRequest, filterSuccessfulResponses, getResponseFromEntry, getSucceededRemoteData } from '../operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all general actions that have to do with the search page
|
* Service that performs all general actions that have to do with the search page
|
||||||
@@ -103,11 +94,18 @@ export class SearchService implements OnDestroy {
|
|||||||
* @returns {Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>} Emits a paginated list with all search results found
|
* @returns {Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>} Emits a paginated list with all search results found
|
||||||
*/
|
*/
|
||||||
search(searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
|
search(searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
|
||||||
const requestObs = this.halService.getEndpoint(this.searchLinkPath).pipe(
|
const hrefObs = this.halService.getEndpoint(this.searchLinkPath).pipe(
|
||||||
map((url: string) => {
|
map((url: string) => {
|
||||||
if (hasValue(searchOptions)) {
|
if (hasValue(searchOptions)) {
|
||||||
url = (searchOptions as PaginatedSearchOptions).toRestUrl(url);
|
return (searchOptions as PaginatedSearchOptions).toRestUrl(url);
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const requestObs = hrefObs.pipe(
|
||||||
|
map((url: string) => {
|
||||||
const request = new this.request(this.requestService.generateRequestId(), url);
|
const request = new this.request(this.requestService.generateRequestId(), url);
|
||||||
|
|
||||||
const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
|
const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
|
||||||
@@ -136,10 +134,11 @@ export class SearchService implements OnDestroy {
|
|||||||
map((sqr: SearchQueryResponse) => {
|
map((sqr: SearchQueryResponse) => {
|
||||||
return sqr.objects
|
return sqr.objects
|
||||||
.filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.indexableObject))
|
.filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.indexableObject))
|
||||||
.map((nsr: NormalizedSearchResult) => {
|
.map((nsr: NormalizedSearchResult) => new GetRequest(this.requestService.generateRequestId(), nsr.indexableObject))
|
||||||
return this.rdb.buildSingle(nsr.indexableObject);
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
|
// Send a request for each item to ensure fresh cache
|
||||||
|
tap((reqs: RestRequest[]) => reqs.forEach((req: RestRequest) => this.requestService.configure(req))),
|
||||||
|
map((reqs: RestRequest[]) => reqs.map((req: RestRequest) => this.rdb.buildSingle(req.href))),
|
||||||
switchMap((input: Array<Observable<RemoteData<DSpaceObject>>>) => this.rdb.aggregate(input)),
|
switchMap((input: Array<Observable<RemoteData<DSpaceObject>>>) => this.rdb.aggregate(input)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -168,11 +167,20 @@ export class SearchService implements OnDestroy {
|
|||||||
|
|
||||||
const payloadObs = observableCombineLatest(tDomainListObs, pageInfoObs).pipe(
|
const payloadObs = observableCombineLatest(tDomainListObs, pageInfoObs).pipe(
|
||||||
map(([tDomainList, pageInfo]) => {
|
map(([tDomainList, pageInfo]) => {
|
||||||
return new PaginatedList(pageInfo, tDomainList);
|
return new PaginatedList(pageInfo, tDomainList.filter((obj) => hasValue(obj)));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
|
return observableCombineLatest(hrefObs, tDomainListObs, requestEntryObs).pipe(
|
||||||
|
switchMap(([href, tDomainList, requestEntry]) => {
|
||||||
|
if (tDomainList.indexOf(undefined) > -1 && requestEntry && requestEntry.completed) {
|
||||||
|
this.requestService.removeByHrefSubstring(href);
|
||||||
|
return this.search(searchOptions)
|
||||||
|
} else {
|
||||||
|
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -29,12 +29,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6">
|
<div class="col-xs-12 col-md-6">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="volumes$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isJournalVolumeOfIssue'"
|
||||||
[label]="'relationships.isSingleVolumeOf' | translate">
|
[label]="'relationships.isSingleVolumeOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
class="mb-1 mt-1"
|
class="mb-1 mt-1"
|
||||||
[items]="publications$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isPublicationOfJournalIssue'"
|
||||||
[label]="'relationships.isPublicationOfJournalIssue' | translate">
|
[label]="'relationships.isPublicationOfJournalIssue' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-generic-item-page-field [item]="item"
|
<ds-generic-item-page-field [item]="item"
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||||
|
|
||||||
@@ -14,19 +12,4 @@ import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/i
|
|||||||
* The component for displaying metadata and relations of an item of the type Journal Issue
|
* The component for displaying metadata and relations of an item of the type Journal Issue
|
||||||
*/
|
*/
|
||||||
export class JournalIssueComponent extends ItemComponent {
|
export class JournalIssueComponent extends ItemComponent {
|
||||||
/**
|
|
||||||
* The volumes related to this journal issue
|
|
||||||
*/
|
|
||||||
volumes$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The publications related to this journal issue
|
|
||||||
*/
|
|
||||||
publications$: Observable<Item[]>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
super.ngOnInit();
|
|
||||||
this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalVolumeOfIssue');
|
|
||||||
this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfJournalIssue');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -17,11 +17,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6">
|
<div class="col-xs-12 col-md-6">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="journals$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isJournalOfVolume'"
|
||||||
[label]="'relationships.isSingleJournalOf' | translate">
|
[label]="'relationships.isSingleJournalOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="issues$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isIssueOfJournalVolume'"
|
||||||
[label]="'relationships.isIssueOf' | translate">
|
[label]="'relationships.isIssueOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-generic-item-page-field [item]="item"
|
<ds-generic-item-page-field [item]="item"
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||||
|
|
||||||
@@ -14,19 +12,4 @@ import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/i
|
|||||||
* The component for displaying metadata and relations of an item of the type Journal Volume
|
* The component for displaying metadata and relations of an item of the type Journal Volume
|
||||||
*/
|
*/
|
||||||
export class JournalVolumeComponent extends ItemComponent {
|
export class JournalVolumeComponent extends ItemComponent {
|
||||||
/**
|
|
||||||
* The journals related to this journal volume
|
|
||||||
*/
|
|
||||||
journals$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The journal issues related to this journal volume
|
|
||||||
*/
|
|
||||||
issues$: Observable<Item[]>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
super.ngOnInit();
|
|
||||||
this.journals$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalOfVolume');
|
|
||||||
this.issues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isIssueOfJournalVolume');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6">
|
<div class="col-xs-12 col-md-6">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="volumes$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isVolumeOfJournal'"
|
||||||
[label]="'relationships.isVolumeOf' | translate">
|
[label]="'relationships.isVolumeOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-generic-item-page-field class="item-page-fields" [item]="item"
|
<ds-generic-item-page-field class="item-page-fields" [item]="item"
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||||
|
|
||||||
@@ -14,13 +12,4 @@ import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/i
|
|||||||
* The component for displaying metadata and relations of an item of the type Journal
|
* The component for displaying metadata and relations of an item of the type Journal
|
||||||
*/
|
*/
|
||||||
export class JournalComponent extends ItemComponent {
|
export class JournalComponent extends ItemComponent {
|
||||||
/**
|
|
||||||
* The volumes related to this journal
|
|
||||||
*/
|
|
||||||
volumes$: Observable<Item[]>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
super.ngOnInit();
|
|
||||||
this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isVolumeOfJournal');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -25,15 +25,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6">
|
<div class="col-xs-12 col-md-6">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="people$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isPersonOfOrgUnit'"
|
||||||
[label]="'relationships.isPersonOf' | translate">
|
[label]="'relationships.isPersonOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="projects$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isProjectOfOrgUnit'"
|
||||||
[label]="'relationships.isProjectOf' | translate">
|
[label]="'relationships.isProjectOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="publications$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isPublicationOfOrgUnit'"
|
||||||
[label]="'relationships.isPublicationOf' | translate">
|
[label]="'relationships.isPublicationOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-generic-item-page-field [item]="item"
|
<ds-generic-item-page-field [item]="item"
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||||
|
|
||||||
@@ -13,26 +11,5 @@ import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/i
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Organisation Unit
|
* The component for displaying metadata and relations of an item of the type Organisation Unit
|
||||||
*/
|
*/
|
||||||
export class OrgunitComponent extends ItemComponent implements OnInit {
|
export class OrgunitComponent extends ItemComponent {
|
||||||
/**
|
|
||||||
* The people related to this organisation unit
|
|
||||||
*/
|
|
||||||
people$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The projects related to this organisation unit
|
|
||||||
*/
|
|
||||||
projects$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The publications related to this organisation unit
|
|
||||||
*/
|
|
||||||
publications$: Observable<Item[]>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
super.ngOnInit();
|
|
||||||
this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfOrgUnit');
|
|
||||||
this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfOrgUnit');
|
|
||||||
this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfOrgUnit');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -25,11 +25,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6">
|
<div class="col-xs-12 col-md-6">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="projects$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isProjectOfPerson'"
|
||||||
[label]="'relationships.isProjectOf' | translate">
|
[label]="'relationships.isProjectOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="orgUnits$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isOrgUnitOfPerson'"
|
||||||
[label]="'relationships.isOrgUnitOf' | translate">
|
[label]="'relationships.isOrgUnitOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-generic-item-page-field [item]="item"
|
<ds-generic-item-page-field [item]="item"
|
||||||
|
@@ -1,11 +1,6 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
|
||||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||||
import { getQueryByRelations } from '../../../../shared/utils/relation-query.utils';
|
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
|
||||||
|
|
||||||
@rendersItemType('Person', ItemViewMode.Detail)
|
@rendersItemType('Person', ItemViewMode.Detail)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -17,45 +12,4 @@ import { RelationshipService } from '../../../../core/data/relationship.service'
|
|||||||
* The component for displaying metadata and relations of an item of the type Person
|
* The component for displaying metadata and relations of an item of the type Person
|
||||||
*/
|
*/
|
||||||
export class PersonComponent extends ItemComponent {
|
export class PersonComponent extends ItemComponent {
|
||||||
/**
|
|
||||||
* The publications related to this person
|
|
||||||
*/
|
|
||||||
publications$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The projects related to this person
|
|
||||||
*/
|
|
||||||
projects$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The organisation units related to this person
|
|
||||||
*/
|
|
||||||
orgUnits$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The applied fixed filter
|
|
||||||
*/
|
|
||||||
fixedFilter$: Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The query used for applying the fixed filter
|
|
||||||
*/
|
|
||||||
fixedFilterQuery: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Inject(ITEM) public item: Item,
|
|
||||||
protected relationshipService: RelationshipService
|
|
||||||
) {
|
|
||||||
super(item, relationshipService);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
super.ngOnInit();
|
|
||||||
this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfAuthor');
|
|
||||||
this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPerson');
|
|
||||||
this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPerson');
|
|
||||||
|
|
||||||
this.fixedFilterQuery = getQueryByRelations('isAuthorOfPublication', this.item.id);
|
|
||||||
this.fixedFilter$ = observableOf('publication');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -11,8 +11,10 @@
|
|||||||
<!--[label]="'project.page.status'">-->
|
<!--[label]="'project.page.status'">-->
|
||||||
<!--</ds-generic-item-page-field>-->
|
<!--</ds-generic-item-page-field>-->
|
||||||
<ds-metadata-representation-list
|
<ds-metadata-representation-list
|
||||||
[label]="'project.page.contributor' | translate"
|
[parentItem]="item"
|
||||||
[representations]="contributors$ | async">
|
[itemType]="'OrgUnit'"
|
||||||
|
[metadataField]="'project.contributor.other'"
|
||||||
|
[label]="'project.page.contributor' | translate">
|
||||||
</ds-metadata-representation-list>
|
</ds-metadata-representation-list>
|
||||||
<ds-generic-item-page-field [item]="item"
|
<ds-generic-item-page-field [item]="item"
|
||||||
[fields]="['project.identifier.funder']"
|
[fields]="['project.identifier.funder']"
|
||||||
@@ -29,15 +31,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-6">
|
<div class="col-xs-12 col-md-6">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="people$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isPersonOfProject'"
|
||||||
[label]="'relationships.isPersonOf' | translate">
|
[label]="'relationships.isPersonOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="publications$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isPublicationOfProject'"
|
||||||
[label]="'relationships.isPublicationOf' | translate">
|
[label]="'relationships.isPublicationOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="orgUnits$ | async"
|
[parentItem]="item"
|
||||||
|
[relationType]="'isOrgUnitOfProject'"
|
||||||
[label]="'relationships.isOrgUnitOf' | translate">
|
[label]="'relationships.isOrgUnitOf' | translate">
|
||||||
</ds-related-items>
|
</ds-related-items>
|
||||||
<ds-generic-item-page-field [item]="item"
|
<ds-generic-item-page-field [item]="item"
|
||||||
|
@@ -1,7 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
|
||||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||||
|
|
||||||
@@ -14,33 +11,5 @@ import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/i
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Project
|
* The component for displaying metadata and relations of an item of the type Project
|
||||||
*/
|
*/
|
||||||
export class ProjectComponent extends ItemComponent implements OnInit {
|
export class ProjectComponent extends ItemComponent {
|
||||||
/**
|
|
||||||
* The contributors related to this project
|
|
||||||
*/
|
|
||||||
contributors$: Observable<MetadataRepresentation[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The people related to this project
|
|
||||||
*/
|
|
||||||
people$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The publications related to this project
|
|
||||||
*/
|
|
||||||
publications$: Observable<Item[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The organisation units related to this project
|
|
||||||
*/
|
|
||||||
orgUnits$: Observable<Item[]>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
super.ngOnInit();
|
|
||||||
this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other');
|
|
||||||
|
|
||||||
this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfProject');
|
|
||||||
this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfProject');
|
|
||||||
this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfProject');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -18,13 +18,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
<ds-metadata-representation-list
|
<ds-metadata-representation-list
|
||||||
[label]="'relationships.isAuthorOf' | translate"
|
[parentItem]="item"
|
||||||
[representations]="authors$ | async">
|
[itemType]="'Person'"
|
||||||
|
[metadataField]="'dc.contributor.author'"
|
||||||
|
[label]="'relationships.isAuthorOf' | translate">
|
||||||
</ds-metadata-representation-list>
|
</ds-metadata-representation-list>
|
||||||
<ds-item-page-file-section [item]="item"></ds-item-page-file-section>
|
<ds-item-page-file-section [item]="item"></ds-item-page-file-section>
|
||||||
<ds-item-page-date-field [item]="item"></ds-item-page-date-field>
|
<ds-item-page-date-field [item]="item"></ds-item-page-date-field>
|
||||||
<ds-item-page-author-field *ngIf="!(authors$ | async)"
|
<ds-item-page-author-field [item]="item"></ds-item-page-author-field>
|
||||||
[item]="item"></ds-item-page-author-field>
|
|
||||||
<ds-generic-item-page-field [item]="item"
|
<ds-generic-item-page-field [item]="item"
|
||||||
[fields]="['journal.title']"
|
[fields]="['journal.title']"
|
||||||
[label]="'publication.page.journal-title'">
|
[label]="'publication.page.journal-title'">
|
||||||
@@ -57,26 +58,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relationships-item-page" *ngIf="(projects$ | async) || (orgUnits$ | async) || (journalIssues$ | async)">
|
<div class="relationships-item-page">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-4" *ngIf="projects$ | async">
|
<div class="col-12 col-md-4">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="projects$ | async"
|
[parentItem]="item"
|
||||||
[label]="'relationships.isProjectOf' | translate">
|
[relationType]="'isProjectOfPublication'"
|
||||||
</ds-related-items>
|
[label]="'relationships.isProjectOf' | translate">
|
||||||
|
</ds-related-items>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4" *ngIf="orgUnits$ | async">
|
<div class="col-12 col-md-4">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="orgUnits$ | async"
|
[parentItem]="item"
|
||||||
[label]="'relationships.isOrgUnitOf' | translate">
|
[relationType]="'isOrgUnitOfPublication'"
|
||||||
</ds-related-items>
|
[label]="'relationships.isOrgUnitOf' | translate">
|
||||||
|
</ds-related-items>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4" *ngIf="journalIssues$ | async">
|
<div class="col-12 col-md-4">
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[items]="journalIssues$ | async"
|
[parentItem]="item"
|
||||||
[label]="'relationships.isJournalIssueOf' | translate">
|
[relationType]="'isJournalIssueOfPublication'"
|
||||||
</ds-related-items>
|
[label]="'relationships.isJournalIssueOf' | translate">
|
||||||
|
</ds-related-items>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user