diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html
index 70e46014d0..1edc00f7eb 100644
--- a/src/app/+item-page/simple/item-types/publication/publication.component.html
+++ b/src/app/+item-page/simple/item-types/publication/publication.component.html
@@ -28,8 +28,10 @@
+ [parentItem]="item"
+ [itemType]="'Person'"
+ [metadataField]="'dc.contributor.author'"
+ [label]="'relationships.isAuthorOf' | translate">
>>;
-
- ngOnInit(): void {
- this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', 'isAuthorOfPublication');
- }
+export class PublicationComponent extends ItemComponent {
}
diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts
index 2a413f482d..f9afa7981b 100644
--- a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts
+++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts
@@ -1,19 +1,14 @@
-import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
-import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
-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 { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
-import { distinctUntilChanged, filter, flatMap, map, switchMap } from 'rxjs/operators';
+import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
import { zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
import { Item } from '../../../../core/shared/item.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { RelationshipService } from '../../../../core/data/relationship.service';
import { PaginatedList } from '../../../../core/data/paginated-list';
-import { of } from 'rxjs/internal/observable/of';
/**
* Operator for comparing arrays using a mapping function
@@ -134,48 +129,6 @@ export const paginatedRelationsToItems = (thisId: string) =>
})
);
-/**
- * Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata
- * The result is wrapped in the original RemoteData and PaginatedList
- * @param parentId 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 = (parentId: string, itemType: string, metadata: MetadataValue[]) =>
- (source: Observable>>): Observable>> =>
- source.pipe(
- flatMap((relRD: RemoteData>) =>
- observableZip(
- ...metadata
- .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
- .map((metadatum: MetadataValue) => {
- if (metadatum.isVirtual) {
- const matchingRels = relRD.payload.page.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 === parentId) {
- return rightItem.payload;
- } else if (rightItem.payload.id === parentId) {
- return leftItem.payload;
- }
- }),
- map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item))
- );
- }
- } else {
- return of(Object.assign(new MetadatumRepresentation(itemType), metadatum));
- }
- })
- ).pipe(
- distinctUntilChanged(compareArraysUsingIds()),
- map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: Object.assign(relRD.payload, { page: representations }) }))
- )
- )
- );
-
/**
* Operator for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup)
* Only relationships where leftItem or rightItem's ID is present in the list provided will be returned
diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts
index 79255f6d7c..ae3d660a4f 100644
--- a/src/app/+item-page/simple/item-types/shared/item.component.ts
+++ b/src/app/+item-page/simple/item-types/shared/item.component.ts
@@ -1,12 +1,6 @@
import { Component, Inject } from '@angular/core';
-import { Observable } from 'rxjs';
-import { PaginatedList } from '../../../../core/data/paginated-list';
-import { RemoteData } from '../../../../core/data/remote-data';
import { Item } from '../../../../core/shared/item.model';
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
-import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
-import { RelationshipService } from '../../../../core/data/relationship.service';
-import { relationsToRepresentations } from './item-relationships-utils';
@Component({
selector: 'ds-item',
@@ -18,24 +12,7 @@ import { relationsToRepresentations } from './item-relationships-utils';
export class ItemComponent {
constructor(
- @Inject(ITEM) public item: Item,
- public relationshipService: RelationshipService
+ @Inject(ITEM) public item: Item
) {}
- /**
- * 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.
- * @param relationshipLabel The label to use to fetch relationships to create MetadataRepresentations for.
- */
- buildRepresentations(itemType: string, metadataField: string, relationshipLabel: string): Observable>> {
- const metadata = this.item.findMetadataSortedByPlace(metadataField);
- const relationships$ = this.relationshipService.getItemRelationshipsByLabel(this.item, relationshipLabel);
-
- return relationships$.pipe(
- relationsToRepresentations(this.item.id, itemType, metadata)
- );
- }
-
}
diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html
index 48eabf8451..e9d2364ca6 100644
--- a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html
+++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html
@@ -1,5 +1,11 @@
- 0" [label]="label">
- 0" [label]="label">
+
+
+
diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts
index f0dc222bf1..4b5553e404 100644
--- a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts
+++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts
@@ -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 { 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({
selector: 'ds-metadata-representation-list',
@@ -8,22 +19,123 @@ import { ItemViewMode } from '../../../shared/items/item-type-decorator';
})
/**
* 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
*/
@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;
+
/**
* The view-mode we're currently on
* @type {ElementViewMode}
*/
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 {
+ 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) =>
+ 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();
+ }
}
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html
index 3ca36b9afd..9ea0676f23 100644
--- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html
+++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html
@@ -11,8 +11,10 @@
+ [parentItem]="item"
+ [itemType]="'OrgUnit'"
+ [metadataField]="'project.contributor.other'"
+ [label]="'project.page.contributor' | translate">
>>;
-
- ngOnInit(): void {
- this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', 'isOrgUnitOfProject');
- }
+export class ProjectComponent extends ItemComponent {
}