mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-16 14:33:03 +00:00
64574: View more/less for metadata-representations + refactoring item pages
This commit is contained in:
@@ -28,8 +28,10 @@
|
|||||||
</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)?.payload?.page">
|
[itemType]="'Person'"
|
||||||
|
[metadataField]="'dc.contributor.author'"
|
||||||
|
[label]="'relationships.isAuthorOf' | translate">
|
||||||
</ds-metadata-representation-list>
|
</ds-metadata-representation-list>
|
||||||
<ds-related-items
|
<ds-related-items
|
||||||
[parentItem]="item"
|
[parentItem]="item"
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
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';
|
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
|
|
||||||
@rendersItemType('Publication', ItemViewMode.Full)
|
@rendersItemType('Publication', ItemViewMode.Full)
|
||||||
@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Full)
|
@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Full)
|
||||||
@@ -17,13 +13,5 @@ import { PaginatedList } from '../../../../core/data/paginated-list';
|
|||||||
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<RemoteData<PaginatedList<MetadataRepresentation>>>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', 'isAuthorOfPublication');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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 { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
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 { 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 { zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
import { of } from 'rxjs/internal/observable/of';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operator for comparing arrays using a mapping function
|
* 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<RemoteData<PaginatedList<Relationship>>>): Observable<RemoteData<PaginatedList<MetadataRepresentation>>> =>
|
|
||||||
source.pipe(
|
|
||||||
flatMap((relRD: RemoteData<PaginatedList<Relationship>>) =>
|
|
||||||
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)
|
* 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
|
* Only relationships where leftItem or rightItem's ID is present in the list provided will be returned
|
||||||
|
@@ -1,12 +1,6 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
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 '../../../../core/shared/item.model';
|
||||||
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 { RelationshipService } from '../../../../core/data/relationship.service';
|
|
||||||
import { relationsToRepresentations } from './item-relationships-utils';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item',
|
selector: 'ds-item',
|
||||||
@@ -18,24 +12,7 @@ import { relationsToRepresentations } from './item-relationships-utils';
|
|||||||
export class ItemComponent {
|
export class ItemComponent {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(ITEM) public item: Item,
|
@Inject(ITEM) public item: Item
|
||||||
public relationshipService: RelationshipService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<RemoteData<PaginatedList<MetadataRepresentation>>> {
|
|
||||||
const metadata = this.item.findMetadataSortedByPlace(metadataField);
|
|
||||||
const relationships$ = this.relationshipService.getItemRelationshipsByLabel(this.item, relationshipLabel);
|
|
||||||
|
|
||||||
return relationships$.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>
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)?.payload?.page">
|
[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']"
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
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';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
|
|
||||||
@rendersItemType('Project', ItemViewMode.Full)
|
@rendersItemType('Project', ItemViewMode.Full)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -15,13 +11,5 @@ import { PaginatedList } from '../../../../core/data/paginated-list';
|
|||||||
/**
|
/**
|
||||||
* 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<RemoteData<PaginatedList<MetadataRepresentation>>>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', 'isOrgUnitOfProject');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user