From 02cf98c75912c2de0ec3f977bedba8643cb17581 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 26 Aug 2019 17:51:42 +0200 Subject: [PATCH 01/12] 64574: relationship-service's getRelatedItemsByLabel returns paginated list using new REST API search endpoint + test cases on publication pages --- .../edit-relationship-list.component.ts | 8 +-- .../publication/publication.component.html | 6 +-- .../publication/publication.component.ts | 22 +++----- .../shared/item-relationships-utils.ts | 29 +++++++++++ .../item-types/shared/item.component.ts | 4 +- src/app/core/data/relationship.service.ts | 52 ++++++++++++++----- .../item-pages/person/person.component.ts | 4 +- 7 files changed, 91 insertions(+), 34 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index 765d6484d4..3a145c99e0 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -4,8 +4,10 @@ import { Observable } from 'rxjs/internal/Observable'; import { FieldUpdate, FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer'; import { RelationshipService } from '../../../../core/data/relationship.service'; import { Item } from '../../../../core/shared/item.model'; -import { switchMap } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { hasValue } from '../../../../shared/empty.util'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; @Component({ 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 * @param label The relationship type's label */ - public getRelatedItemsByLabel(label: string): Observable { + public getRelatedItemsByLabel(label: string): Observable>> { return this.relationshipService.getRelatedItemsByLabel(this.item, label); } @@ -73,7 +75,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges { */ public getUpdatesByLabel(label: string): Observable { return this.getRelatedItemsByLabel(label).pipe( - switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, items)) + switchMap((itemsRD) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, itemsRD.payload.page)) ) } 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 abf5225c27..aae269cdbe 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 @@ -32,15 +32,15 @@ [representations]="authors$ | async"> diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts index 81e2726e0c..1d5a942559 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -8,6 +8,8 @@ import { import { ItemComponent } from '../shared/item.component'; import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { getRelatedItemsByTypeLabel } from '../shared/item-relationships-utils'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; @rendersItemType('Publication', ItemViewMode.Full) @rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Full) @@ -26,17 +28,17 @@ export class PublicationComponent extends ItemComponent implements OnInit { /** * The projects related to this publication */ - projects$: Observable; + projects$: Observable>>; /** * The organisation units related to this publication */ - orgUnits$: Observable; + orgUnits$: Observable>>; /** * The journal issues related to this publication */ - journalIssues$: Observable; + journalIssues$: Observable>>; ngOnInit(): void { super.ngOnInit(); @@ -45,17 +47,9 @@ export class PublicationComponent extends ItemComponent implements OnInit { this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author'); - this.projects$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfPublication') - ); - - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfPublication') - ); - - this.journalIssues$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isJournalIssueOfPublication') - ); + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication'); + this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication'); } } 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 b4eda2abfb..c63900663d 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 @@ -13,6 +13,7 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; 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'; /** * Operator for comparing arrays using a mapping function @@ -99,6 +100,34 @@ export const relationsToItems = (thisId: string) => distinctUntilChanged(compareArraysUsingIds()), ); +export const paginatedRelationsToItems = (thisId: string) => + (source: Observable>>): Observable>> => + source.pipe( + getSucceededRemoteData(), + switchMap((relationshipsRD: RemoteData>) => { + return observableZip( + ...relationshipsRD.payload.page.map((rel: Relationship) => observableCombineLatest(rel.leftItem, rel.rightItem)) + ).pipe( + map((arr) => + arr + .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; + } + }) + .filter((item: Item) => hasValue(item)) + ), + distinctUntilChanged(compareArraysUsingIds()), + map((relatedItems: Item[]) => + Object.assign(relationshipsRD, { payload: { page: relatedItems } }) + ) + ) + }) + ); + /** * Operator for turning a list of relationships and their relationship-types into a list of relevant items by relationship label * @param thisId The item's id of which the relations belong to 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 556496fe49..e3b9ce59a8 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 @@ -15,6 +15,7 @@ import { MetadatumRepresentation } from '../../../../core/shared/metadata-repres 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 @@ -68,7 +69,8 @@ export class ItemComponent implements OnInit { resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>; constructor( - @Inject(ITEM) public item: Item + @Inject(ITEM) public item: Item, + public relationshipService: RelationshipService ) {} ngOnInit(): void { diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 1699b6a27d..5791ab2165 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -10,7 +10,7 @@ import { getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators'; -import { DeleteRequest, RestRequest } from './request.models'; +import { DeleteRequest, FindAllOptions, RestRequest } from './request.models'; import { Observable } from 'rxjs/internal/Observable'; import { RestResponse } from '../cache/response.models'; import { Item } from '../shared/item.model'; @@ -22,23 +22,42 @@ import { zip as observableZip } from 'rxjs'; import { PaginatedList } from './paginated-list'; import { ItemDataService } from './item-data.service'; import { - compareArraysUsingIds, filterRelationsByTypeLabel, + compareArraysUsingIds, filterRelationsByTypeLabel, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils'; import { ObjectCacheService } from '../cache/object-cache.service'; +import { DataService } from './data.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { SearchParam } from '../cache/models/search-param.model'; /** * The service handling all relationship requests */ @Injectable() -export class RelationshipService { +export class RelationshipService extends DataService { protected linkPath = 'relationships'; + protected forceBypassCache = false; - constructor(protected requestService: RequestService, - protected halService: HALEndpointService, + constructor(protected itemService: ItemDataService, + protected requestService: RequestService, protected rdbService: RemoteDataBuildService, - protected itemService: ItemDataService, - protected objectCache: ObjectCacheService) { + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected halService: HALEndpointService, + protected objectCache: ObjectCacheService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } + + getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable { + return this.halService.getEndpoint(linkPath); } /** @@ -208,11 +227,20 @@ export class RelationshipService { * @param item * @param label */ - getRelatedItemsByLabel(item: Item, label: string): Observable { - return this.getItemResolvedRelsAndTypes(item).pipe( - filterRelationsByTypeLabel(label), - relationsToItems(item.uuid) - ); + getRelatedItemsByLabel(item: Item, label: string): Observable>> { + return this.getItemRelationshipsByLabel(item, label).pipe(paginatedRelationsToItems(item.uuid)); + } + + /** + * Resolve a given item's relationships into related items, filtered by a relationship label + * and return the items as an array + * @param item + * @param label + */ + getItemRelationshipsByLabel(item: Item, label: string): Observable>> { + const options = new FindAllOptions(); + options.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + return this.searchBy('byLabel', options); } /** diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts index 8b36175b96..cd43c0b08b 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts @@ -7,6 +7,7 @@ import { SearchFixedFilterService } from '../../../../+search-page/search-filter import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component'; import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils'; +import { RelationshipService } from '../../../../core/data/relationship.service'; @rendersItemType('Person', ItemViewMode.Full) @Component({ @@ -45,9 +46,10 @@ export class PersonComponent extends ItemComponent { constructor( @Inject(ITEM) public item: Item, + public relationshipService: RelationshipService, private fixedFilterService: SearchFixedFilterService ) { - super(item); + super(item, relationshipService); } ngOnInit(): void { super.ngOnInit(); From 4b55d7507cf376a593e46216fbec825b073c1902 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 11:18:12 +0200 Subject: [PATCH 02/12] 64574: Refactor item-pages to use paginated-lists and relationship-service --- .../publication/publication.component.html | 2 +- .../publication/publication.component.ts | 17 ++---- .../item-types/shared/item.component.ts | 60 ++++--------------- .../journal-issue.component.html | 4 +- .../journal-issue/journal-issue.component.ts | 20 ++----- .../journal-volume.component.html | 4 +- .../journal-volume.component.ts | 20 ++----- .../item-pages/journal/journal.component.html | 2 +- .../item-pages/journal/journal.component.ts | 14 ++--- .../item-pages/orgunit/orgunit.component.html | 6 +- .../item-pages/orgunit/orgunit.component.ts | 31 ++++------ .../item-pages/person/person.component.html | 4 +- .../item-pages/person/person.component.ts | 55 ++++------------- .../item-pages/project/project.component.html | 8 +-- .../item-pages/project/project.component.ts | 32 ++++------ 15 files changed, 78 insertions(+), 201 deletions(-) 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 aae269cdbe..b61fa6bbc1 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 @@ -29,7 +29,7 @@
+ [representations]="(authors$ | async)?.payload?.page"> ; + authors$: Observable>>; /** * The projects related to this publication @@ -41,16 +40,10 @@ export class PublicationComponent extends ItemComponent implements OnInit { journalIssues$: Observable>>; ngOnInit(): void { - super.ngOnInit(); + this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', 'isAuthorOfPublication'); - if (this.resolvedRelsAndTypes$) { - - 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'); - - } + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication'); + this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication'); } } 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 e3b9ce59a8..18aa229611 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,20 +1,16 @@ -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 { filter, flatMap, map } from 'rxjs/operators'; 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 { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; 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'; /** @@ -24,15 +20,15 @@ import { RelationshipService } from '../../../../core/data/relationship.service' * @param metadata The list of original Metadatum objects */ export const relationsToRepresentations = (thisId: string, itemType: string, metadata: MetadataValue[]) => - (source: Observable): Observable => + (source: Observable>>): Observable>> => source.pipe( - flatMap((rels: Relationship[]) => + flatMap((relRD: RemoteData>) => 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); + 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( @@ -51,6 +47,8 @@ export const relationsToRepresentations = (thisId: string, itemType: string, met return of(Object.assign(new MetadatumRepresentation(itemType), metadatum)); } }) + ).pipe( + map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) ) ) ); @@ -62,59 +60,25 @@ export const relationsToRepresentations = (thisId: string, itemType: string, met /** * A generic component for displaying metadata and relations of an item */ -export class ItemComponent implements OnInit { - /** - * Resolved relationships and types together in one observable - */ - resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>; +export class ItemComponent { constructor( @Inject(ITEM) public item: Item, public relationshipService: RelationshipService ) {} - ngOnInit(): void { - const relationships$ = this.item.relationships; - if (relationships$) { - const relsCurrentPage$ = relationships$.pipe( - filter((rd: RemoteData>) => rd.hasSucceeded), - getRemoteDataPayload(), - map((pl: PaginatedList) => pl.page), - distinctUntilChanged(compareArraysUsingIds()) - ); - - const relTypesCurrentPage$ = relsCurrentPage$.pipe( - flatMap((rels: Relationship[]) => - observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe( - map(([...arr]: Array>) => arr.map((d: RemoteData) => d.payload)) - ) - ), - distinctUntilChanged(compareArraysUsingIds()) - ); - - this.resolvedRelsAndTypes$ = observableCombineLatest( - relsCurrentPage$, - relTypesCurrentPage$ - ); - } - } - /** * 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): Observable { + buildRepresentations(itemType: string, metadataField: string, relationshipLabel: string): Observable>> { const metadata = this.item.findMetadataSortedByPlace(metadataField); - const relsCurrentPage$ = this.item.relationships.pipe( - getSucceededRemoteData(), - getRemoteDataPayload(), - map((pl: PaginatedList) => pl.page), - distinctUntilChanged(compareArraysUsingIds()) - ); + const relationships$ = this.relationshipService.getItemRelationshipsByLabel(this.item, relationshipLabel); - return relsCurrentPage$.pipe( + return relationships$.pipe( relationsToRepresentations(this.item.id, itemType, metadata) ); } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index eebd3e03c8..b69827cd96 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -29,12 +29,12 @@
; + volumes$: Observable>>; /** * The publications related to this journal issue */ - publications$: Observable; + publications$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.volumes$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isJournalVolumeOfIssue') - ); - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfJournalIssue') - ); - } + this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalVolumeOfIssue'); + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfJournalIssue'); } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index 83626c7ae7..902b0e9d73 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -17,11 +17,11 @@
; + journals$: Observable>>; /** * The journal issues related to this journal volume */ - issues$: Observable; + issues$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.journals$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isJournalOfVolume') - ); - this.issues$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isIssueOfJournalVolume') - ); - } + this.journals$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalOfVolume'); + this.issues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isIssueOfJournalVolume'); } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index a82d3c5df6..ee212d9be6 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -21,7 +21,7 @@
; + volumes$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.volumes$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isVolumeOfJournal') - ); - } + this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isVolumeOfJournal'); } } diff --git a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html index 92ac3eac30..91e9fa0374 100644 --- a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html @@ -25,15 +25,15 @@
; + people$: Observable>>; /** * The projects related to this organisation unit */ - projects$: Observable; + projects$: Observable>>; /** * The publications related to this organisation unit */ - publications$: Observable; + publications$: Observable>>; ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.people$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPersonOfOrgUnit') - ); - - this.projects$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfOrgUnit') - ); - - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfOrgUnit') - ); - } - }} + this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfOrgUnit'); + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfOrgUnit'); + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfOrgUnit'); + } +} diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index 04d7b9e062..ba6b83bbf6 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -25,11 +25,11 @@
; + publications$: Observable>>; /** * The projects related to this person */ - projects$: Observable; + projects$: Observable>>; /** * The organisation units related to this person */ - orgUnits$: Observable; + orgUnits$: Observable>>; - /** - * The applied fixed filter - */ - fixedFilter$: Observable; - - /** - * The query used for applying the fixed filter - */ - fixedFilterQuery: string; - - constructor( - @Inject(ITEM) public item: Item, - public relationshipService: RelationshipService, - private fixedFilterService: SearchFixedFilterService - ) { - super(item, relationshipService); - } ngOnInit(): void { - super.ngOnInit(); - - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfAuthor') - ); - - this.projects$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfPerson') - ); - - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfPerson') - ); - - this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id); - this.fixedFilter$ = observableOf('publication'); - } + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfAuthor'); + this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPerson'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPerson'); } } 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 4e9a130b8c..c6eb0690ce 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 @@ -12,7 +12,7 @@ + [representations]="(contributors$ | async)?.payload?.page">
; + contributors$: Observable>>; /** * The people related to this project */ - people$: Observable; + people$: Observable>>; /** * The publications related to this project */ - publications$: Observable; + publications$: Observable>>; /** * The organisation units related to this project */ - orgUnits$: Observable; + orgUnits$: Observable>>; ngOnInit(): void { - super.ngOnInit(); + this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', 'isOrgUnitOfProject'); - if (isNotEmpty(this.resolvedRelsAndTypes$)) { - this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other'); - - this.people$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPersonOfProject') - ); - - this.publications$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfProject') - ); - - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfProject') - ); - } + this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfProject'); + this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfProject'); + this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfProject'); } } From bd54d47037c73f81c510dd25d3bb4d7ca26d1a06 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 11:36:32 +0200 Subject: [PATCH 03/12] 64574: Further refactoring of item-component, mantis publication and JSDocs --- .../shared/item-relationships-utils.ts | 41 +++++++--------- .../item-types/shared/item.component.ts | 49 +------------------ .../publication/publication.component.html | 6 +-- 3 files changed, 24 insertions(+), 72 deletions(-) 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 c63900663d..36c605fdc1 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 @@ -7,13 +7,13 @@ 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, tap } from 'rxjs/operators'; -import { of as observableOf, zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; -import { ItemDataService } from '../../../../core/data/item-data.service'; +import { distinctUntilChanged, filter, 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 @@ -100,6 +100,12 @@ export const relationsToItems = (thisId: string) => distinctUntilChanged(compareArraysUsingIds()), ); +/** + * Operator for turning a paginated list of relationships into a paginated list of the relevant items + * The result is wrapped in the original RemoteData and PaginatedList + * @param {string} thisId The item's id of which the relations belong to + * @returns {(source: Observable) => Observable} + */ export const paginatedRelationsToItems = (thisId: string) => (source: Observable>>): Observable>> => source.pipe( @@ -128,39 +134,27 @@ export const paginatedRelationsToItems = (thisId: string) => }) ); -/** - * Operator for turning a list of relationships and their relationship-types into a list of relevant items by relationship label - * @param thisId The item's id of which the relations belong to - * @param label The label of the relationship-type to filter on - * @param side Filter only on one side of the relationship (for example: child-parent relationships) - */ -export const getRelatedItemsByTypeLabel = (thisId: string, label: string) => - (source: Observable<[Relationship[], RelationshipType[]]>): Observable => - source.pipe( - filterRelationsByTypeLabel(label, thisId), - relationsToItems(thisId) - ); - /** * 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 - * @param ids The ItemDataService to use for fetching Items from the Rest API */ -export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[], ids: ItemDataService) => - (source: Observable): Observable => +export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[]) => + (source: Observable>>): Observable>> => source.pipe( - flatMap((rels: Relationship[]) => + flatMap((relRD: RemoteData>) => 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); + 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; @@ -172,9 +166,12 @@ export const relationsToRepresentations = (parentId: string, itemType: string, m ); } } else { - return observableOf(Object.assign(new MetadatumRepresentation(itemType), metadatum)); + return of(Object.assign(new MetadatumRepresentation(itemType), metadatum)); } }) + ).pipe( + distinctUntilChanged(compareArraysUsingIds()), + map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) ) ) ); 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 18aa229611..79255f6d7c 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,57 +1,12 @@ import { Component, Inject } from '@angular/core'; -import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; -import { filter, flatMap, map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; -import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; 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 { 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 { 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>>): 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 === 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)); - } - }) - ).pipe( - map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) - ) - ) - ); +import { relationsToRepresentations } from './item-relationships-utils'; @Component({ selector: 'ds-item', diff --git a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html index 6b6f484183..bb3d6f8bd0 100644 --- a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html +++ b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html @@ -62,19 +62,19 @@
From 4afb35c53ef3e8ff9602e3a798f372fb0d00ca1c Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 14:12:39 +0200 Subject: [PATCH 04/12] 64574: View-more/less for related-items + refactoring item pages --- resources/i18n/en.json | 2 + .../publication/publication.component.html | 9 ++- .../publication/publication.component.ts | 20 ------ .../shared/item-relationships-utils.ts | 4 +- .../related-items/related-items-component.ts | 65 +++++++++++++++++-- .../related-items.component.html | 10 ++- src/app/core/data/relationship.service.ts | 17 +++-- .../journal-issue.component.html | 6 +- .../journal-issue/journal-issue.component.ts | 18 ----- .../journal-volume.component.html | 6 +- .../journal-volume.component.ts | 18 ----- .../item-pages/journal/journal.component.html | 3 +- .../item-pages/journal/journal.component.ts | 12 ---- .../item-pages/orgunit/orgunit.component.html | 9 ++- .../item-pages/orgunit/orgunit.component.ts | 28 +------- .../item-pages/person/person.component.html | 6 +- .../item-pages/person/person.component.ts | 24 ------- .../item-pages/project/project.component.html | 9 ++- .../item-pages/project/project.component.ts | 20 ------ 19 files changed, 117 insertions(+), 169 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index e91b2776c1..e660189602 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -290,6 +290,8 @@ "item.page.link.full": "Full item page", "item.page.link.simple": "Simple item page", "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.uri": "URI", "item.select.confirm": "Confirm selected", 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 b61fa6bbc1..70e46014d0 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 @@ -32,15 +32,18 @@ [representations]="(authors$ | async)?.payload?.page"> diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts index fa4b0988d5..16c81a1d14 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -1,6 +1,5 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { Item } from '../../../../core/shared/item.model'; import { DEFAULT_ITEM_TYPE, ItemViewMode, rendersItemType @@ -24,26 +23,7 @@ export class PublicationComponent extends ItemComponent implements OnInit { */ authors$: Observable>>; - /** - * The projects related to this publication - */ - projects$: Observable>>; - - /** - * The organisation units related to this publication - */ - orgUnits$: Observable>>; - - /** - * The journal issues related to this publication - */ - journalIssues$: Observable>>; - ngOnInit(): void { this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', 'isAuthorOfPublication'); - - this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPublication'); - this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPublication'); - this.journalIssues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalIssueOfPublication'); } } 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 36c605fdc1..2a413f482d 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 @@ -128,7 +128,7 @@ export const paginatedRelationsToItems = (thisId: string) => ), distinctUntilChanged(compareArraysUsingIds()), map((relatedItems: Item[]) => - Object.assign(relationshipsRD, { payload: { page: relatedItems } }) + Object.assign(relationshipsRD, { payload: Object.assign(relationshipsRD.payload, { page: relatedItems } )}) ) ) }) @@ -171,7 +171,7 @@ export const relationsToRepresentations = (parentId: string, itemType: string, m }) ).pipe( distinctUntilChanged(compareArraysUsingIds()), - map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: { page: representations } })) + map((representations: MetadataRepresentation[]) => Object.assign(relRD, { payload: Object.assign(relRD.payload, { page: representations }) })) ) ) ); diff --git a/src/app/+item-page/simple/related-items/related-items-component.ts b/src/app/+item-page/simple/related-items/related-items-component.ts index 7b54d7316a..838508524c 100644 --- a/src/app/+item-page/simple/related-items/related-items-component.ts +++ b/src/app/+item-page/simple/related-items/related-items-component.ts @@ -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 { 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({ 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 - * 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) */ @Input() label: string; + /** + * The list of related items + */ + items$: Observable>>; + + /** + * Search options for displaying all elements in a list + */ + allOptions = Object.assign(new FindAllOptions(), { elementsPerPage: 9999 }); + /** * The view-mode we're currently on * @type {ElementViewMode} */ viewMode = ItemViewMode.Element; + + /** + * 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; + } } diff --git a/src/app/+item-page/simple/related-items/related-items.component.html b/src/app/+item-page/simple/related-items/related-items.component.html index 4b284ad63c..20d4c6c38c 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.html +++ b/src/app/+item-page/simple/related-items/related-items.component.html @@ -1,5 +1,11 @@ - - + + + diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 5791ab2165..db4af49d8a 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -226,9 +226,10 @@ export class RelationshipService extends DataService { * and return the items as an array * @param item * @param label + * @param options */ - getRelatedItemsByLabel(item: Item, label: string): Observable>> { - return this.getItemRelationshipsByLabel(item, label).pipe(paginatedRelationsToItems(item.uuid)); + getRelatedItemsByLabel(item: Item, label: string, options?: FindAllOptions): Observable>> { + return this.getItemRelationshipsByLabel(item, label, options).pipe(paginatedRelationsToItems(item.uuid)); } /** @@ -236,11 +237,15 @@ export class RelationshipService extends DataService { * and return the items as an array * @param item * @param label + * @param options */ - getItemRelationshipsByLabel(item: Item, label: string): Observable>> { - const options = new FindAllOptions(); - options.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; - return this.searchBy('byLabel', options); + getItemRelationshipsByLabel(item: Item, label: string, options?: FindAllOptions): Observable>> { + let findAllOptions = new FindAllOptions(); + if (options) { + findAllOptions = Object.assign(new FindAllOptions(), options); + } + findAllOptions.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + return this.searchBy('byLabel', findAllOptions); } /** diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index b69827cd96..8db50e78c4 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -29,12 +29,14 @@
>>; - - /** - * The publications related to this journal issue - */ - publications$: Observable>>; - - ngOnInit(): void { - this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalVolumeOfIssue'); - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfJournalIssue'); - } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index 902b0e9d73..150037eccb 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -17,11 +17,13 @@
>>; - - /** - * The journal issues related to this journal volume - */ - issues$: Observable>>; - - ngOnInit(): void { - this.journals$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isJournalOfVolume'); - this.issues$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isIssueOfJournalVolume'); - } } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index ee212d9be6..d22933a657 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -21,7 +21,8 @@
>>; - - ngOnInit(): void { - this.volumes$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isVolumeOfJournal'); - } } diff --git a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html index 91e9fa0374..a3d2fedb10 100644 --- a/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html @@ -25,15 +25,18 @@
>>; - - /** - * The projects related to this organisation unit - */ - projects$: Observable>>; - - /** - * The publications related to this organisation unit - */ - publications$: Observable>>; - - ngOnInit(): void { - this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfOrgUnit'); - this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfOrgUnit'); - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfOrgUnit'); - } +export class OrgunitComponent extends ItemComponent { } diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index ba6b83bbf6..3f0ca90368 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -25,11 +25,13 @@
>>; - - /** - * The projects related to this person - */ - projects$: Observable>>; - - /** - * The organisation units related to this person - */ - orgUnits$: Observable>>; - - ngOnInit(): void { - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfAuthor'); - this.projects$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isProjectOfPerson'); - this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfPerson'); - } } 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 c6eb0690ce..3ca36b9afd 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 @@ -29,15 +29,18 @@
>>; - /** - * The people related to this project - */ - people$: Observable>>; - - /** - * The publications related to this project - */ - publications$: Observable>>; - - /** - * The organisation units related to this project - */ - orgUnits$: Observable>>; - ngOnInit(): void { this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', 'isOrgUnitOfProject'); - - this.people$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPersonOfProject'); - this.publications$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isPublicationOfProject'); - this.orgUnits$ = this.relationshipService.getRelatedItemsByLabel(this.item, 'isOrgUnitOfProject'); } } From a641b20db7c583c19c46579d684249bd046b99b7 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 15:58:23 +0200 Subject: [PATCH 05/12] 64574: View more/less for metadata-representations + refactoring item pages --- .../publication/publication.component.html | 6 +- .../publication/publication.component.ts | 16 +-- .../shared/item-relationships-utils.ts | 49 +------ .../item-types/shared/item.component.ts | 25 +--- ...etadata-representation-list.component.html | 10 +- .../metadata-representation-list.component.ts | 122 +++++++++++++++++- .../item-pages/project/project.component.html | 6 +- .../item-pages/project/project.component.ts | 16 +-- 8 files changed, 139 insertions(+), 111 deletions(-) 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 @@ - - + + + 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 { } From 8a1fd78811f3474a46926e2ac0dcb08745f3487b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 27 Aug 2019 17:38:53 +0200 Subject: [PATCH 06/12] 64574: Tests intermediate commit --- .../edit-relationship-list.component.spec.ts | 2 +- .../related-items.component.html | 4 +- .../related-items.component.spec.ts | 48 ++++++++++++++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 3748ebca9d..ca6c1fdd2e 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -97,7 +97,7 @@ describe('EditRelationshipListComponent', () => { relationshipService = jasmine.createSpyObj('relationshipService', { - getRelatedItemsByLabel: observableOf([author1, author2]), + getRelatedItemsByLabel: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [author1, author2]))), } ); diff --git a/src/app/+item-page/simple/related-items/related-items.component.html b/src/app/+item-page/simple/related-items/related-items.component.html index 20d4c6c38c..a4baee9825 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.html +++ b/src/app/+item-page/simple/related-items/related-items.component.html @@ -2,10 +2,10 @@ -
+ -
+ diff --git a/src/app/+item-page/simple/related-items/related-items.component.spec.ts b/src/app/+item-page/simple/related-items/related-items.component.spec.ts index 1896f46015..fa8891dce3 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.spec.ts +++ b/src/app/+item-page/simple/related-items/related-items.component.spec.ts @@ -2,14 +2,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { RelatedItemsComponent } from './related-items-component'; import { Item } from '../../../core/shared/item.model'; -import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; import { createRelationshipsObservable } from '../item-types/shared/item.component.spec'; -import { of as observableOf } from 'rxjs'; 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(), { bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])), metadata: [], @@ -21,16 +26,26 @@ const mockItem2: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); const mockItems = [mockItem1, mockItem2]; +const relationType = 'isItemOfItem'; +let relationshipService: RelationshipService; -describe('RelatedItemsComponent', () => { +fdescribe('RelatedItemsComponent', () => { let comp: RelatedItemsComponent; let fixture: ComponentFixture; beforeEach(async(() => { + relationshipService = jasmine.createSpyObj('relationshipService', + { + getRelatedItemsByLabel: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockItems)), + } + ); + TestBed.configureTestingModule({ - imports: [], + imports: [TranslateModule.forRoot()], declarations: [RelatedItemsComponent], - providers: [], + providers: [ + { provide: RelationshipService, useValue: relationshipService } + ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(RelatedItemsComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} @@ -40,7 +55,8 @@ describe('RelatedItemsComponent', () => { beforeEach(async(() => { fixture = TestBed.createComponent(RelatedItemsComponent); comp = fixture.componentInstance; - comp.items = mockItems; + comp.parentItem = parentItem; + comp.relationType = relationType; fixture.detectChanges(); })); @@ -49,4 +65,24 @@ describe('RelatedItemsComponent', () => { 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); + }); + }); + + 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); + }); + }); + }); From f4f099995e80a6dd9f4d183b2bf57005f910ec6f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Aug 2019 11:13:54 +0200 Subject: [PATCH 07/12] 64574: Tests and small bugfix --- ...data-representation-list.component.spec.ts | 103 ++++++++++++++++-- .../related-items.component.spec.ts | 10 +- src/app/core/data/relationship.service.ts | 7 +- 3 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts index f02625e8c7..120e846523 100644 --- a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts +++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts @@ -2,23 +2,72 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { MetadataRepresentationListComponent } from './metadata-representation-list.component'; -import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; -import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; +import { RelationshipService } from '../../../core/data/relationship.service'; +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 metadataRepresentation1 = new MetadatumRepresentation(itemType); -const metadataRepresentation2 = new ItemMetadataRepresentation(); -const representations = [metadataRepresentation1, metadataRepresentation2]; +const itemType = 'Person'; +const metadataField = 'dc.contributor.author'; +const parentItem: Item = Object.assign(new Item(), { + 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', () => { let comp: MetadataRepresentationListComponent; let fixture: ComponentFixture; + relationshipService = jasmine.createSpyObj('relationshipService', + { + findById: createSuccessfulRemoteDataObject$(relation) + } + ); + beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [], + imports: [TranslateModule.forRoot()], declarations: [MetadataRepresentationListComponent], - providers: [], + providers: [ + { provide: RelationshipService, useValue: relationshipService } + ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(MetadataRepresentationListComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} @@ -28,13 +77,45 @@ describe('MetadataRepresentationListComponent', () => { beforeEach(async(() => { fixture = TestBed.createComponent(MetadataRepresentationListComponent); comp = fixture.componentInstance; - comp.representations = representations; + comp.parentItem = parentItem; + comp.itemType = itemType; + comp.metadataField = metadataField; 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')); - 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); + }); }); }); diff --git a/src/app/+item-page/simple/related-items/related-items.component.spec.ts b/src/app/+item-page/simple/related-items/related-items.component.spec.ts index fa8891dce3..6637091f02 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.spec.ts +++ b/src/app/+item-page/simple/related-items/related-items.component.spec.ts @@ -29,7 +29,7 @@ const mockItems = [mockItem1, mockItem2]; const relationType = 'isItemOfItem'; let relationshipService: RelationshipService; -fdescribe('RelatedItemsComponent', () => { +describe('RelatedItemsComponent', () => { let comp: RelatedItemsComponent; let fixture: ComponentFixture; @@ -73,6 +73,10 @@ fdescribe('RelatedItemsComponent', () => { 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', () => { @@ -83,6 +87,10 @@ fdescribe('RelatedItemsComponent', () => { 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); + }); }); }); diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index db4af49d8a..2638935028 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -244,7 +244,12 @@ export class RelationshipService extends DataService { if (options) { findAllOptions = Object.assign(new FindAllOptions(), options); } - findAllOptions.searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + const searchParams = [ new SearchParam('label', label), new SearchParam('dso', item.id) ]; + if (findAllOptions.searchParams) { + findAllOptions.searchParams = [...findAllOptions.searchParams, ...searchParams]; + } else { + findAllOptions.searchParams = searchParams; + } return this.searchBy('byLabel', findAllOptions); } From 713c40451f5dc74a40b2dd43e4d78587430e3160 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Aug 2019 11:32:04 +0200 Subject: [PATCH 08/12] 64574: Mantis publication template fix --- .../publication/publication.component.html | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html index bb3d6f8bd0..a6886cf1ac 100644 --- a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html +++ b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html @@ -18,13 +18,14 @@
+ [parentItem]="item" + [itemType]="'Person'" + [metadataField]="'dc.contributor.author'" + [label]="'relationships.isAuthorOf' | translate"> - + @@ -57,26 +58,29 @@
-
+
-
- - +
+ +
-
- - +
+ +
-
- - +
+ +
From 76636933e4d0910bd11322841f0130623e8e34f2 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Aug 2019 12:01:24 +0200 Subject: [PATCH 09/12] 64574: Test fixes --- .../item-types/shared/item.component.spec.ts | 111 ------------------ .../core/data/relationship.service.spec.ts | 21 ++-- 2 files changed, 9 insertions(+), 123 deletions(-) diff --git a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts index 3f525e6a25..41d05afd1b 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts @@ -317,115 +317,4 @@ describe('ItemComponent', () => { }); }); - describe('when calling buildRepresentations', () => { - let comp: ItemComponent; - let fixture: ComponentFixture; - - 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; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: MockTranslateLoader - } - }), BrowserAnimationsModule], - declarations: [ItemComponent, VarDirective], - providers: [ - {provide: ITEM, useValue: mockItem} - ], - - 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); - }); - }); - }) - }); diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts index 0ced517d74..bbd950ef5c 100644 --- a/src/app/core/data/relationship.service.spec.ts +++ b/src/app/core/data/relationship.service.spec.ts @@ -79,11 +79,16 @@ describe('RelationshipService', () => { function initTestService() { return new RelationshipService( - requestService, - halService, - rdbService, itemService, - objectCache + requestService, + rdbService, + null, + null, + halService, + objectCache, + null, + null, + null ); } @@ -142,14 +147,6 @@ 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> { From 7233482fe76a2fdf745acee7d95e33fc135d903b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 29 Aug 2019 10:32:28 +0200 Subject: [PATCH 10/12] 64574: Fix AoT error --- .../simple/item-types/publication/publication.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1edc00f7eb..deac919072 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 @@ -8,7 +8,7 @@ - + From adcefbae17c716787aaeed3970191eb9563d370f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 5 Sep 2019 17:34:13 +0200 Subject: [PATCH 11/12] 64574: Update mantis item pages with new template --- .../related-items/related-items-component.ts | 29 ++++++++++- .../publication/publication.component.html | 39 +++++++------- .../journal-issue.component.html | 40 ++++++++------- .../journal-volume.component.html | 32 ++++++------ .../item-pages/journal/journal.component.html | 22 ++++---- .../item-pages/orgunit/orgunit.component.html | 48 ++++++++--------- .../item-pages/person/person.component.html | 46 +++++++---------- .../item-pages/project/project.component.html | 51 ++++++++++--------- 8 files changed, 163 insertions(+), 144 deletions(-) diff --git a/src/app/+item-page/simple/related-items/related-items-component.ts b/src/app/+item-page/simple/related-items/related-items-component.ts index 838508524c..5b4376bab0 100644 --- a/src/app/+item-page/simple/related-items/related-items-component.ts +++ b/src/app/+item-page/simple/related-items/related-items-component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; import { ItemViewMode } from '../../../shared/items/item-type-decorator'; import { Observable } from 'rxjs/internal/Observable'; @@ -6,6 +6,9 @@ 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'; +import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators'; +import { hasNoValue, hasValueOperator } from '../../../shared/empty.util'; +import { Subscription } from 'rxjs/internal/Subscription'; @Component({ selector: 'ds-related-items', @@ -16,7 +19,7 @@ import { FindAllOptions } from '../../../core/data/request.models'; * This component is used for displaying relations between items * It expects a parent item and relationship type, as well as a label to display on top */ -export class RelatedItemsComponent implements OnInit { +export class RelatedItemsComponent implements OnInit, OnDestroy { /** * The parent of the list of related items to display */ @@ -39,6 +42,11 @@ export class RelatedItemsComponent implements OnInit { */ @Input() label: string; + /** + * Completely hide the component until there's at least one item visible + */ + @HostBinding('class.d-none') hidden = true; + /** * The list of related items */ @@ -60,11 +68,19 @@ export class RelatedItemsComponent implements OnInit { */ showingAll = false; + /** + * Subscription on items used to update the "hidden" property of this component + */ + itemSub: Subscription; + constructor(public relationshipService: RelationshipService) { } ngOnInit(): void { this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options); + this.itemSub = this.items$.subscribe((itemsRD: RemoteData>) => { + this.hidden = !(itemsRD.hasSucceeded && itemsRD.payload && itemsRD.payload.page.length > 0); + }); } /** @@ -82,4 +98,13 @@ export class RelatedItemsComponent implements OnInit { this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options); this.showingAll = false; } + + /** + * Unsubscribe from the item subscription + */ + ngOnDestroy(): void { + if (this.itemSub) { + this.itemSub.unsubscribe(); + } + } } diff --git a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html index a6886cf1ac..de74a75d2b 100644 --- a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html +++ b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html @@ -61,27 +61,24 @@
-
- - -
-
- - -
-
- - -
+ + + + + +
diff --git a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index a25a474eb0..b34be8aec2 100644 --- a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -26,25 +26,29 @@ [label]="'journalissue.page.journal-title'">
+ +
@@ -53,21 +57,21 @@
-
+
-
- - -
-
- - -
+ + + +
diff --git a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index c20e9a775a..99651377cb 100644 --- a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -23,17 +23,17 @@
@@ -42,21 +42,21 @@
-
+
-
- - -
-
- - -
+ + + +
diff --git a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index ef827af590..ecc36c53bc 100644 --- a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -22,21 +22,21 @@
@@ -45,15 +45,15 @@
-
+
-
- - -
+ +
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html index 6bb925c93f..d5db61b7a1 100644 --- a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html +++ b/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html @@ -23,25 +23,25 @@
@@ -50,27 +50,27 @@
-
+
-
- - -
-
- - -
-
- - -
+ + + + + +
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html b/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html index 54d7962b97..851a24e961 100644 --- a/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -17,38 +17,30 @@

{{'person.page.titleprefix' | translate}} + [mdValues]="[item?.firstMetadata('person.familyName'), item?.firstMetadata('person.givenName')]" [separator]="', '">

- - - -
@@ -57,21 +49,21 @@
-
+
-
- - -
-
- - -
+ + + +
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/project/project.component.html b/themes/mantis/app/entity-groups/research-entities/item-pages/project/project.component.html index aa4a107247..9b8f1532ae 100644 --- a/themes/mantis/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/themes/mantis/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -22,9 +22,10 @@
- +
@@ -58,27 +59,27 @@
-
+
-
- - -
-
- - -
-
- - -
+ + + + + +
From 6ffd791f120493d8e5839f1cae168d1571046c20 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 6 Sep 2019 11:35:20 +0200 Subject: [PATCH 12/12] 64574: Further mantis template fixes --- .../publication/publication.component.html | 2 +- .../journal-issue.component.html | 2 +- .../journal-volume.component.html | 2 +- .../item-pages/journal/journal.component.html | 2 +- .../item-pages/orgunit/orgunit.component.html | 4 ++-- .../item-pages/person/person.component.html | 10 ++++++++- .../item-pages/project/project.component.html | 22 +++++++++---------- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html index de74a75d2b..b7e2dbad39 100644 --- a/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html +++ b/themes/mantis/app/+item-page/simple/item-types/publication/publication.component.html @@ -4,7 +4,7 @@