mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
Merge branch 'master' into w2p-65195_dynamic-component-refactoring
This commit is contained in:
@@ -405,6 +405,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",
|
||||
|
||||
|
@@ -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]))),
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -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<Item[]> {
|
||||
public getRelatedItemsByLabel(label: string): Observable<RemoteData<PaginatedList<Item>>> {
|
||||
return this.relationshipService.getRelatedItemsByLabel(this.item, label);
|
||||
}
|
||||
|
||||
@@ -73,7 +75,7 @@ export class EditRelationshipListComponent implements OnInit, OnChanges {
|
||||
*/
|
||||
public getUpdatesByLabel(label: string): Observable<FieldUpdates> {
|
||||
return this.getRelatedItemsByLabel(label).pipe(
|
||||
switchMap((items: Item[]) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, items))
|
||||
switchMap((itemsRD) => this.objectUpdatesService.getFieldUpdatesExclusive(this.url, itemsRD.payload.page))
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
</ds-metadata-field-wrapper>
|
||||
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
|
||||
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
||||
<ds-item-page-author-field *ngIf="!(authors$ | async)" [item]="object"></ds-item-page-author-field>
|
||||
<ds-item-page-author-field [item]="object"></ds-item-page-author-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journal.title']"
|
||||
[label]="'publication.page.journal-title'">
|
||||
@@ -28,19 +28,24 @@
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-metadata-representation-list
|
||||
[label]="'relationships.isAuthorOf' | translate"
|
||||
[representations]="authors$ | async">
|
||||
[parentItem]="item"
|
||||
[itemType]="'Person'"
|
||||
[metadataField]="'dc.contributor.author'"
|
||||
[label]="'relationships.isAuthorOf' | translate">
|
||||
</ds-metadata-representation-list>
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isProjectOfPublication'"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isOrgUnitOfPublication'"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="journalIssues$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isJournalIssueOfPublication'"
|
||||
[label]="'relationships.isJournalIssueOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-item-page-abstract-field [item]="object"></ds-item-page-abstract-field>
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import {
|
||||
DEFAULT_ITEM_TYPE, ItemViewMode,
|
||||
rendersItemType
|
||||
} from '../../../../shared/items/item-type-decorator';
|
||||
import { ItemComponent } from '../shared/item.component';
|
||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { getRelatedItemsByTypeLabel } from '../shared/item-relationships-utils';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
/**
|
||||
@@ -19,49 +18,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
templateUrl: './publication.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PublicationComponent extends ItemComponent implements OnInit {
|
||||
/**
|
||||
* The authors related to this publication
|
||||
*/
|
||||
authors$: Observable<MetadataRepresentation[]>;
|
||||
|
||||
/**
|
||||
* The projects related to this publication
|
||||
*/
|
||||
projects$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The organisation units related to this publication
|
||||
*/
|
||||
orgUnits$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The journal issues related to this publication
|
||||
*/
|
||||
journalIssues$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
if (this.resolvedRelsAndTypes$) {
|
||||
|
||||
this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author');
|
||||
|
||||
this.projects$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isProjectOfPublication')
|
||||
);
|
||||
|
||||
this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isOrgUnitOfPublication')
|
||||
);
|
||||
|
||||
this.journalIssues$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isJournalIssueOfPublication')
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
export class PublicationComponent extends ItemComponent {
|
||||
}
|
||||
|
@@ -1,18 +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, 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, 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';
|
||||
|
||||
/**
|
||||
* Operator for comparing arrays using a mapping function
|
||||
@@ -100,53 +96,37 @@ export const relationsToItems = (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)
|
||||
* 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<Relationship[]>) => Observable<Item[]>}
|
||||
*/
|
||||
export const getRelatedItemsByTypeLabel = (thisId: string, label: string) =>
|
||||
(source: Observable<[Relationship[], RelationshipType[]]>): Observable<Item[]> =>
|
||||
export const paginatedRelationsToItems = (thisId: string) =>
|
||||
(source: Observable<RemoteData<PaginatedList<Relationship>>>): Observable<RemoteData<PaginatedList<Item>>> =>
|
||||
source.pipe(
|
||||
filterRelationsByTypeLabel(label, thisId),
|
||||
relationsToItems(thisId)
|
||||
);
|
||||
|
||||
/**
|
||||
* Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata
|
||||
* @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<Relationship[]>): Observable<MetadataRepresentation[]> =>
|
||||
source.pipe(
|
||||
flatMap((rels: Relationship[]) =>
|
||||
observableZip(
|
||||
...metadata
|
||||
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
||||
.map((metadatum: MetadataValue) => {
|
||||
if (metadatum.isVirtual) {
|
||||
const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue);
|
||||
if (matchingRels.length > 0) {
|
||||
const matchingRel = matchingRels[0];
|
||||
return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe(
|
||||
map(([leftItem, rightItem]) => {
|
||||
if (leftItem.payload.id === parentId) {
|
||||
getSucceededRemoteData(),
|
||||
switchMap((relationshipsRD: RemoteData<PaginatedList<Relationship>>) => {
|
||||
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 === parentId) {
|
||||
} else if (rightItem.payload.id === thisId) {
|
||||
return leftItem.payload;
|
||||
}
|
||||
}),
|
||||
map((item: Item) => Object.assign(new ItemMetadataRepresentation(metadatum), item))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return observableOf(Object.assign(new MetadatumRepresentation(itemType), metadatum));
|
||||
}
|
||||
})
|
||||
.filter((item: Item) => hasValue(item))
|
||||
),
|
||||
distinctUntilChanged(compareArraysUsingIds()),
|
||||
map((relatedItems: Item[]) =>
|
||||
Object.assign(relationshipsRD, { payload: Object.assign(relationshipsRD.payload, { page: relatedItems } )})
|
||||
)
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
|
@@ -314,112 +314,4 @@ describe('ItemComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when calling buildRepresentations', () => {
|
||||
let comp: ItemComponent;
|
||||
let fixture: ComponentFixture<ItemComponent>;
|
||||
|
||||
const metadataField = 'dc.contributor.author';
|
||||
const relatedItem = Object.assign(new Item(), {
|
||||
id: '2',
|
||||
metadata: Object.assign(new MetadataMap(), {
|
||||
'dc.title': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'related item'
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
const mockItem = Object.assign(new Item(), {
|
||||
id: '1',
|
||||
uuid: '1',
|
||||
metadata: new MetadataMap()
|
||||
});
|
||||
mockItem.relationships = createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [
|
||||
Object.assign(new Relationship(), {
|
||||
uuid: '123',
|
||||
id: '123',
|
||||
leftItem: createSuccessfulRemoteDataObject$(mockItem),
|
||||
rightItem: createSuccessfulRemoteDataObject$(relatedItem),
|
||||
relationshipType: createSuccessfulRemoteDataObject$(new RelationshipType())
|
||||
})
|
||||
]));
|
||||
mockItem.metadata[metadataField] = [
|
||||
{
|
||||
value: 'Second value',
|
||||
place: 1
|
||||
},
|
||||
{
|
||||
value: 'Third value',
|
||||
place: 2,
|
||||
authority: 'virtual::123'
|
||||
},
|
||||
{
|
||||
value: 'First value',
|
||||
place: 0
|
||||
},
|
||||
{
|
||||
value: 'Fourth value',
|
||||
place: 3,
|
||||
authority: '123'
|
||||
}
|
||||
] as MetadataValue[];
|
||||
const mockItemDataService = Object.assign({
|
||||
findById: (id) => {
|
||||
if (id === relatedItem.id) {
|
||||
return createSuccessfulRemoteDataObject$(relatedItem)
|
||||
}
|
||||
}
|
||||
}) as ItemDataService;
|
||||
|
||||
let representations: Observable<MetadataRepresentation[]>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
}), BrowserAnimationsModule],
|
||||
declarations: [ItemComponent, VarDirective],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ItemComponent, {
|
||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(ItemComponent);
|
||||
comp = fixture.componentInstance;
|
||||
comp.object = mockItem;
|
||||
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('Third value');
|
||||
expect(reps[3].getValue()).toEqual('Fourth value');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have created the correct MetadatumRepresentation and ItemMetadataRepresentation objects for the correct Metadata', () => {
|
||||
representations.subscribe((reps: MetadataRepresentation[]) => {
|
||||
expect(reps[0] instanceof MetadatumRepresentation).toEqual(true);
|
||||
expect(reps[1] instanceof MetadatumRepresentation).toEqual(true);
|
||||
expect(reps[2] instanceof ItemMetadataRepresentation).toEqual(true);
|
||||
expect(reps[3] instanceof MetadatumRepresentation).toEqual(true);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
@@ -1,14 +1,6 @@
|
||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
||||
import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { distinctUntilChanged, 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 { Component, Inject } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { compareArraysUsingIds, relationsToRepresentations } from './item-relationships-utils';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item',
|
||||
@@ -17,57 +9,10 @@ import { compareArraysUsingIds, relationsToRepresentations } from './item-relati
|
||||
/**
|
||||
* 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[]]>;
|
||||
@Input() object: Item;
|
||||
export class ItemComponent {
|
||||
|
||||
ngOnInit(): void {
|
||||
const relationships$ = this.object.relationships;
|
||||
if (relationships$) {
|
||||
const relsCurrentPage$ = relationships$.pipe(
|
||||
filter((rd: RemoteData<PaginatedList<Relationship>>) => rd.hasSucceeded),
|
||||
getRemoteDataPayload(),
|
||||
map((pl: PaginatedList<Relationship>) => pl.page),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
|
||||
const relTypesCurrentPage$ = relsCurrentPage$.pipe(
|
||||
flatMap((rels: Relationship[]) =>
|
||||
observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe(
|
||||
map(([...arr]: Array<RemoteData<RelationshipType>>) => arr.map((d: RemoteData<RelationshipType>) => 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.
|
||||
*/
|
||||
buildRepresentations(itemType: string, metadataField: string): Observable<MetadataRepresentation[]> {
|
||||
const metadata = this.object.findMetadataSortedByPlace(metadataField);
|
||||
const relsCurrentPage$ = this.object.relationships.pipe(
|
||||
getSucceededRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
map((pl: PaginatedList<Relationship>) => pl.page),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
|
||||
return relsCurrentPage$.pipe(
|
||||
relationsToRepresentations(this.object.id, itemType, metadata)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
@Inject(ITEM) public item: Item
|
||||
) {}
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,11 @@
|
||||
<ds-metadata-field-wrapper *ngIf="representations && representations.length > 0" [label]="label">
|
||||
<ds-metadata-representation-loader *ngFor="let rep of representations"
|
||||
<ds-metadata-field-wrapper *ngIf="representations$ && (representations$ | async)?.length > 0" [label]="label">
|
||||
<ds-metadata-representation-loader *ngFor="let rep of (representations$ | async)"
|
||||
[mdRepresentation]="rep">
|
||||
</ds-metadata-representation-loader>
|
||||
<div *ngIf="(representations$ | async)?.length < total" class="mt-2">
|
||||
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
||||
</div>
|
||||
<div *ngIf="limit > originalLimit" class="mt-2">
|
||||
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
||||
</div>
|
||||
</ds-metadata-field-wrapper>
|
||||
|
@@ -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(Object.assign({}));
|
||||
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<MetadataRepresentationListComponent>;
|
||||
|
||||
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 ds-metadata-representation-loader components', () => {
|
||||
const fields = fixture.debugElement.queryAll(By.css('ds-metadata-representation-loader'));
|
||||
expect(fields.length).toBe(representations.length);
|
||||
expect(fields.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should initialize the original limit', () => {
|
||||
expect(comp.originalLimit).toEqual(comp.limit);
|
||||
});
|
||||
|
||||
describe('when viewMore is called', () => {
|
||||
beforeEach(() => {
|
||||
comp.viewMore();
|
||||
});
|
||||
|
||||
it('should set the limit to a high number in order to retrieve all metadata representations', () => {
|
||||
expect(comp.limit).toBeGreaterThanOrEqual(999);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when viewLess is called', () => {
|
||||
let originalLimit;
|
||||
|
||||
beforeEach(() => {
|
||||
// Store the original value of limit
|
||||
originalLimit = comp.limit;
|
||||
// Set limit to a random number
|
||||
comp.limit = 458;
|
||||
comp.viewLess();
|
||||
});
|
||||
|
||||
it('should reset the limit to the original value', () => {
|
||||
expect(comp.limit).toEqual(originalLimit);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -1,5 +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',
|
||||
@@ -7,16 +19,117 @@ import { MetadataRepresentation } from '../../../core/shared/metadata-representa
|
||||
})
|
||||
/**
|
||||
* 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<MetadataRepresentation[]>;
|
||||
|
||||
/**
|
||||
* The originally provided limit
|
||||
* Used for resetting the limit to the original value when collapsing the list
|
||||
*/
|
||||
originalLimit: number;
|
||||
|
||||
/**
|
||||
* The total amount of metadata values available
|
||||
*/
|
||||
total: number;
|
||||
|
||||
constructor(public relationshipService: RelationshipService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.originalLimit = this.limit;
|
||||
this.setRepresentations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the metadata representations
|
||||
*/
|
||||
setRepresentations() {
|
||||
const metadata = this.parentItem.findMetadataSortedByPlace(this.metadataField);
|
||||
this.total = metadata.length;
|
||||
this.representations$ = this.resolveMetadataRepresentations(metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of metadata values to a list of metadata representations
|
||||
* @param metadata
|
||||
*/
|
||||
resolveMetadataRepresentations(metadata: MetadataValue[]): Observable<MetadataRepresentation[]> {
|
||||
return observableZip(
|
||||
...metadata
|
||||
.slice(0, this.limit)
|
||||
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
||||
.map((metadatum: MetadataValue) => {
|
||||
if (metadatum.isVirtual) {
|
||||
return this.relationshipService.findById(metadatum.virtualValue).pipe(
|
||||
getSucceededRemoteData(),
|
||||
switchMap((relRD: RemoteData<Relationship>) =>
|
||||
observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe(
|
||||
filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded),
|
||||
map(([leftItem, rightItem]) => {
|
||||
if (leftItem.payload.id === this.parentItem.id) {
|
||||
return rightItem.payload;
|
||||
} else if (rightItem.payload.id === this.parentItem.id) {
|
||||
return leftItem.payload;
|
||||
}
|
||||
}),
|
||||
map((item: Item) => Object.assign(new ItemMetadataRepresentation(metadatum), item))
|
||||
)
|
||||
));
|
||||
} else {
|
||||
return observableOf(Object.assign(new MetadatumRepresentation(this.itemType), metadatum));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the list to display all metadata representations
|
||||
*/
|
||||
viewMore() {
|
||||
this.limit = 9999;
|
||||
this.setRepresentations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse the list to display the originally displayed metadata representations
|
||||
*/
|
||||
viewLess() {
|
||||
this.limit = this.originalLimit;
|
||||
this.setRepresentations();
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,14 @@
|
||||
import { Component, Input } 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';
|
||||
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';
|
||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||
|
||||
@Component({
|
||||
@@ -9,22 +18,94 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||
})
|
||||
/**
|
||||
* 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, OnDestroy {
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Completely hide the component until there's at least one item visible
|
||||
*/
|
||||
@HostBinding('class.d-none') hidden = true;
|
||||
|
||||
/**
|
||||
* The list of related items
|
||||
*/
|
||||
items$: Observable<RemoteData<PaginatedList<Item>>>;
|
||||
|
||||
/**
|
||||
* Search options for displaying all elements in a list
|
||||
*/
|
||||
allOptions = Object.assign(new FindAllOptions(), { elementsPerPage: 9999 });
|
||||
|
||||
/**
|
||||
* The view-mode we're currently on
|
||||
* @type {ElementViewMode}
|
||||
*/
|
||||
viewMode = ViewMode.ListElement;
|
||||
|
||||
/**
|
||||
* Whether or not the list is currently expanded to show all related items
|
||||
*/
|
||||
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<PaginatedList<Item>>) => {
|
||||
this.hidden = !(itemsRD.hasSucceeded && itemsRD.payload && itemsRD.payload.page.length > 0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from the item subscription
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (this.itemSub) {
|
||||
this.itemSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,11 @@
|
||||
<ds-metadata-field-wrapper *ngIf="items && items.length > 0" [label]="label">
|
||||
<ds-listable-object-component-loader *ngFor="let item of items"
|
||||
<ds-metadata-field-wrapper *ngIf="(items$ | async)?.payload?.page?.length > 0" [label]="label">
|
||||
<ds-listable-object-component-loader *ngFor="let item of (items$ | async)?.payload?.page"
|
||||
[object]="item" [viewMode]="viewMode">
|
||||
</ds-listable-object-component-loader>
|
||||
<div *ngIf="(items$ | async)?.payload?.page?.length < (items$ | async)?.payload?.totalElements" class="mt-2" id="view-more">
|
||||
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
||||
</div>
|
||||
<div *ngIf="showingAll" class="mt-2" id="view-less">
|
||||
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
||||
</div>
|
||||
</ds-metadata-field-wrapper>
|
||||
|
@@ -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', () => {
|
||||
let comp: RelatedItemsComponent;
|
||||
let fixture: ComponentFixture<RelatedItemsComponent>;
|
||||
|
||||
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,32 @@ 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);
|
||||
});
|
||||
|
||||
it('should set showingAll to true', () => {
|
||||
expect(comp.showingAll).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when viewLess is called', () => {
|
||||
beforeEach(() => {
|
||||
comp.viewLess();
|
||||
});
|
||||
|
||||
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => {
|
||||
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.options);
|
||||
});
|
||||
|
||||
it('should set showingAll to false', () => {
|
||||
expect(comp.showingAll).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -14,6 +14,7 @@ import { PageInfo } from '../shared/page-info.model';
|
||||
import { DeleteRequest } from './request.models';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
|
||||
|
||||
describe('RelationshipService', () => {
|
||||
let service: RelationshipService;
|
||||
@@ -22,12 +23,6 @@ describe('RelationshipService', () => {
|
||||
const restEndpointURL = 'https://rest.api/';
|
||||
const relationshipsEndpointURL = `${restEndpointURL}/relationships`;
|
||||
const halService: any = new HALEndpointServiceStub(restEndpointURL);
|
||||
const rdbService = getMockRemoteDataBuildService();
|
||||
const objectCache = Object.assign({
|
||||
/* tslint:disable:no-empty */
|
||||
remove: () => {}
|
||||
/* tslint:enable:no-empty */
|
||||
}) as ObjectCacheService;
|
||||
|
||||
const relationshipType = Object.assign(new RelationshipType(), {
|
||||
id: '1',
|
||||
@@ -72,17 +67,30 @@ describe('RelationshipService', () => {
|
||||
relationship2.rightItem = getRemotedataObservable(item);
|
||||
const relatedItems = [relatedItem1, relatedItem2];
|
||||
|
||||
const buildList$ = createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [relatedItems]));
|
||||
const rdbService = getMockRemoteDataBuildService(undefined, buildList$);
|
||||
const objectCache = Object.assign({
|
||||
/* tslint:disable:no-empty */
|
||||
remove: () => {}
|
||||
/* tslint:enable:no-empty */
|
||||
}) as ObjectCacheService;
|
||||
|
||||
const itemService = jasmine.createSpyObj('itemService', {
|
||||
findById: (uuid) => new RemoteData(false, false, true, undefined, relatedItems.filter((relatedItem) => relatedItem.id === uuid)[0])
|
||||
});
|
||||
|
||||
function initTestService() {
|
||||
return new RelationshipService(
|
||||
requestService,
|
||||
halService,
|
||||
rdbService,
|
||||
itemService,
|
||||
objectCache
|
||||
requestService,
|
||||
rdbService,
|
||||
null,
|
||||
null,
|
||||
halService,
|
||||
objectCache,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,7 +152,7 @@ describe('RelationshipService', () => {
|
||||
describe('getRelatedItemsByLabel', () => {
|
||||
it('should return the related items by label', () => {
|
||||
service.getRelatedItemsByLabel(item, relationshipType.rightwardType).subscribe((result) => {
|
||||
expect(result).toEqual(relatedItems);
|
||||
expect(result.payload.page).toEqual(relatedItems);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
@@ -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<Relationship> {
|
||||
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<CoreState>,
|
||||
protected halService: HALEndpointService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<Relationship>) {
|
||||
super();
|
||||
}
|
||||
|
||||
getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||
return this.halService.getEndpoint(linkPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,12 +226,31 @@ export class RelationshipService {
|
||||
* and return the items as an array
|
||||
* @param item
|
||||
* @param label
|
||||
* @param options
|
||||
*/
|
||||
getRelatedItemsByLabel(item: Item, label: string): Observable<Item[]> {
|
||||
return this.getItemResolvedRelsAndTypes(item).pipe(
|
||||
filterRelationsByTypeLabel(label),
|
||||
relationsToItems(item.uuid)
|
||||
);
|
||||
getRelatedItemsByLabel(item: Item, label: string, options?: FindAllOptions): Observable<RemoteData<PaginatedList<Item>>> {
|
||||
return this.getItemRelationshipsByLabel(item, label, options).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
|
||||
* @param options
|
||||
*/
|
||||
getItemRelationshipsByLabel(item: Item, label: string, options?: FindAllOptions): Observable<RemoteData<PaginatedList<Relationship>>> {
|
||||
let findAllOptions = new FindAllOptions();
|
||||
if (options) {
|
||||
findAllOptions = Object.assign(new FindAllOptions(), options);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -29,12 +29,14 @@
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-items
|
||||
[items]="volumes$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isJournalVolumeOfIssue'"
|
||||
[label]="'relationships.isSingleVolumeOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
class="mb-1 mt-1"
|
||||
[items]="publications$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isPublicationOfJournalIssue'"
|
||||
[label]="'relationships.isPublicationOfJournalIssue' | translate">
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
|
@@ -1,10 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
@listableObjectComponent('JournalIssue', ViewMode.StandalonePage)
|
||||
@@ -17,29 +13,4 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
* The component for displaying metadata and relations of an item of the type Journal Issue
|
||||
*/
|
||||
export class JournalIssueComponent extends ItemComponent {
|
||||
/**
|
||||
* The volumes related to this journal issue
|
||||
*/
|
||||
volumes$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The publications related to this journal issue
|
||||
*/
|
||||
publications$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* Initialize the instance variables
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
|
||||
this.volumes$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isJournalVolumeOfIssue')
|
||||
);
|
||||
this.publications$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isPublicationOfJournalIssue')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,11 +17,13 @@
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-items
|
||||
[items]="journals$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isJournalOfVolume'"
|
||||
[label]="'relationships.isSingleJournalOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="issues$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isIssueOfJournalVolume'"
|
||||
[label]="'relationships.isIssueOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
|
@@ -1,10 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
@listableObjectComponent('JournalVolume', ViewMode.StandalonePage)
|
||||
@@ -17,29 +13,4 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
* The component for displaying metadata and relations of an item of the type Journal Volume
|
||||
*/
|
||||
export class JournalVolumeComponent extends ItemComponent {
|
||||
/**
|
||||
* The journals related to this journal volume
|
||||
*/
|
||||
journals$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The journal issues related to this journal volume
|
||||
*/
|
||||
issues$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* Initialize the instance variables
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
|
||||
this.journals$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isJournalOfVolume')
|
||||
);
|
||||
this.issues$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isIssueOfJournalVolume')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,8 @@
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-items
|
||||
[items]="volumes$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isVolumeOfJournal'"
|
||||
[label]="'relationships.isVolumeOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field class="item-page-fields" [item]="object"
|
||||
|
@@ -1,10 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
@listableObjectComponent('Journal', ViewMode.StandalonePage)
|
||||
@@ -17,21 +13,4 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
* The component for displaying metadata and relations of an item of the type Journal
|
||||
*/
|
||||
export class JournalComponent extends ItemComponent {
|
||||
/**
|
||||
* The volumes related to this journal
|
||||
*/
|
||||
volumes$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* Initialize the instance variables
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
|
||||
this.volumes$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isVolumeOfJournal')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,15 +25,18 @@
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-items
|
||||
[items]="people$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isPersonOfOrgUnit'"
|
||||
[label]="'relationships.isPersonOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isProjectOfOrgUnit'"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="publications$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isPublicationOfOrgUnit'"
|
||||
[label]="'relationships.isPublicationOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
|
@@ -1,10 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { Component } from '@angular/core';
|
||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
@listableObjectComponent('OrgUnit', ViewMode.StandalonePage)
|
||||
@@ -16,36 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item of the type Organisation Unit
|
||||
*/
|
||||
export class OrgUnitComponent extends ItemComponent implements OnInit {
|
||||
/**
|
||||
* The people related to this organisation unit
|
||||
*/
|
||||
people$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The projects related to this organisation unit
|
||||
*/
|
||||
projects$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The publications related to this organisation unit
|
||||
*/
|
||||
publications$: Observable<Item[]>;
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
|
||||
this.people$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isPersonOfOrgUnit')
|
||||
);
|
||||
|
||||
this.projects$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isProjectOfOrgUnit')
|
||||
);
|
||||
|
||||
this.publications$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isPublicationOfOrgUnit')
|
||||
);
|
||||
export class OrgUnitComponent extends ItemComponent {
|
||||
}
|
||||
}}
|
||||
|
@@ -25,11 +25,13 @@
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isProjectOfPerson'"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isOrgUnitOfPerson'"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
|
@@ -1,11 +1,6 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Observable , of as observableOf } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { Component } from '@angular/core';
|
||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
@listableObjectComponent('Person', ViewMode.StandalonePage)
|
||||
@@ -18,55 +13,4 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
* The component for displaying metadata and relations of an item of the type Person
|
||||
*/
|
||||
export class PersonComponent extends ItemComponent {
|
||||
/**
|
||||
* The publications related to this person
|
||||
*/
|
||||
publications$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The projects related to this person
|
||||
*/
|
||||
projects$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The organisation units related to this person
|
||||
*/
|
||||
orgUnits$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The applied fixed filter
|
||||
*/
|
||||
fixedFilter$: Observable<string>;
|
||||
|
||||
/**
|
||||
* The query used for applying the fixed filter
|
||||
*/
|
||||
fixedFilterQuery: string;
|
||||
|
||||
constructor(
|
||||
private fixedFilterService: SearchFixedFilterService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
|
||||
this.publications$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isPublicationOfAuthor')
|
||||
);
|
||||
|
||||
this.projects$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isProjectOfPerson')
|
||||
);
|
||||
|
||||
this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isOrgUnitOfPerson')
|
||||
);
|
||||
|
||||
this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.object.id);
|
||||
this.fixedFilter$ = observableOf('publication');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,8 +11,10 @@
|
||||
<!--[label]="'project.page.status'">-->
|
||||
<!--</ds-generic-item-page-field>-->
|
||||
<ds-metadata-representation-list
|
||||
[label]="'project.page.contributor' | translate"
|
||||
[representations]="contributors$ | async">
|
||||
[parentItem]="item"
|
||||
[itemType]="'OrgUnit'"
|
||||
[metadataField]="'project.contributor.other'"
|
||||
[label]="'project.page.contributor' | translate">
|
||||
</ds-metadata-representation-list>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['project.identifier.funder']"
|
||||
@@ -29,15 +31,18 @@
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-items
|
||||
[items]="people$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isPersonOfProject'"
|
||||
[label]="'relationships.isPersonOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="publications$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isPublicationOfProject'"
|
||||
[label]="'relationships.isPublicationOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isOrgUnitOfProject'"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
|
@@ -1,11 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { Component } from '@angular/core';
|
||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
@listableObjectComponent('Project', ViewMode.StandalonePage)
|
||||
@@ -17,44 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item of the type Project
|
||||
*/
|
||||
export class ProjectComponent extends ItemComponent implements OnInit {
|
||||
/**
|
||||
* The contributors related to this project
|
||||
*/
|
||||
contributors$: Observable<MetadataRepresentation[]>;
|
||||
|
||||
/**
|
||||
* The people related to this project
|
||||
*/
|
||||
people$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The publications related to this project
|
||||
*/
|
||||
publications$: Observable<Item[]>;
|
||||
|
||||
/**
|
||||
* The organisation units related to this project
|
||||
*/
|
||||
orgUnits$: Observable<Item[]>;
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
|
||||
this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other');
|
||||
|
||||
this.people$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isPersonOfProject')
|
||||
);
|
||||
|
||||
this.publications$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isPublicationOfProject')
|
||||
);
|
||||
|
||||
this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
|
||||
getRelatedItemsByTypeLabel(this.object.id, 'isOrgUnitOfProject')
|
||||
);
|
||||
}
|
||||
}
|
||||
export class ProjectComponent extends ItemComponent {
|
||||
}
|
||||
|
@@ -6,8 +6,10 @@ import { RequestEntry } from '../../core/data/request.reducer';
|
||||
import { hasValue } from '../empty.util';
|
||||
import { NormalizedObject } from '../../core/cache/models/normalized-object.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../testing/utils';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../core/shared/page-info.model';
|
||||
|
||||
export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observable<RemoteData<any>>): RemoteDataBuildService {
|
||||
export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observable<RemoteData<any>>, buildList$?: Observable<RemoteData<PaginatedList<any>>>): RemoteDataBuildService {
|
||||
return {
|
||||
toRemoteDataObservable: (requestEntry$: Observable<RequestEntry>, payload$: Observable<any>) => {
|
||||
|
||||
@@ -20,7 +22,14 @@ export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observab
|
||||
}
|
||||
},
|
||||
buildSingle: (href$: string | Observable<string>) => createSuccessfulRemoteDataObject$({}),
|
||||
build: (normalized: NormalizedObject<any>) => Object.create({})
|
||||
build: (normalized: NormalizedObject<any>) => Object.create({}),
|
||||
buildList: (href$: string | Observable<string>) => {
|
||||
if (hasValue(buildList$)) {
|
||||
return buildList$;
|
||||
} else {
|
||||
return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), []))
|
||||
}
|
||||
}
|
||||
} as RemoteDataBuildService;
|
||||
|
||||
}
|
||||
|
@@ -18,13 +18,14 @@
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-metadata-representation-list
|
||||
[label]="'relationships.isAuthorOf' | translate"
|
||||
[representations]="authors$ | async">
|
||||
[parentItem]="object"
|
||||
[itemType]="'Person'"
|
||||
[metadataField]="'dc.contributor.author'"
|
||||
[label]="'relationships.isAuthorOf' | translate">
|
||||
</ds-metadata-representation-list>
|
||||
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
|
||||
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
||||
<ds-item-page-author-field *ngIf="!(authors$ | async)"
|
||||
[item]="object"></ds-item-page-author-field>
|
||||
<ds-item-page-author-field [item]="object"></ds-item-page-author-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journal.title']"
|
||||
[label]="'publication.page.journal-title'">
|
||||
@@ -57,27 +58,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relationships-item-page" *ngIf="(projects$ | async) || (orgUnits$ | async) || (journalIssues$ | async)">
|
||||
<div class="relationships-item-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4" *ngIf="projects$ | async">
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isProjectOfPublication'"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="orgUnits$ | async">
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isOrgUnitOfPublication'"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="journalIssues$ | async">
|
||||
<ds-related-items
|
||||
[items]="journalIssues$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isJournalIssueOfPublication'"
|
||||
[label]="'relationships.isJournalIssueOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -26,25 +26,29 @@
|
||||
[label]="'journalissue.page.journal-title'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journalissue.identifier.number']"
|
||||
[fields]="['publicationvolume.volumeNumber']"
|
||||
[label]="'journalvolume.page.volume'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['publicationissue.issueNumber']"
|
||||
[label]="'journalissue.page.number'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journal.identifier.issn']"
|
||||
[fields]="['creativeworkseries.issn']"
|
||||
[label]="'journalissue.page.journal-issn'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journalissue.issuedate']"
|
||||
[fields]="['creativework.datePublished']"
|
||||
[label]="'journalissue.page.issuedate'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journalissue.identifier.description']"
|
||||
[fields]="['dc.description']"
|
||||
[label]="'journalissue.page.description'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journalissue.identifier.keyword']"
|
||||
[fields]="['creativework.keywords']"
|
||||
[label]="'journalissue.page.keyword'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
@@ -53,21 +57,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relationships-item-page" *ngIf="(volumes$ | async) || (publications$ | async)">
|
||||
<div class="relationships-item-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4" *ngIf="volumes$ | async">
|
||||
<ds-related-items
|
||||
[items]="volumes$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isJournalVolumeOfIssue'"
|
||||
[label]="'relationships.isSingleVolumeOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="publications$ | async">
|
||||
<ds-related-items
|
||||
[items]="publications$ | async"
|
||||
class="mb-1 mt-1 col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isPublicationOfJournalIssue'"
|
||||
[label]="'relationships.isPublicationOfJournalIssue' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||
<ds-metadata-field-wrapper>
|
||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="item.getThumbnail() | async"></ds-thumbnail>
|
||||
</ds-metadata-field-wrapper>
|
||||
<div>
|
||||
<a class="btn btn-secondary"
|
||||
@@ -23,17 +23,17 @@
|
||||
<div class="col-12 col-md-6">
|
||||
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journalvolume.identifier.volume']"
|
||||
[fields]="['publicationvolume.volumeNumber']"
|
||||
[label]="'journalvolume.page.volume'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journalvolume.issuedate']"
|
||||
[fields]="['creativework.datePublished']"
|
||||
[label]="'journalvolume.page.issuedate'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journalvolume.identifier.description']"
|
||||
[fields]="['dc.description']"
|
||||
[label]="'journalvolume.page.description'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
@@ -42,21 +42,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relationships-item-page" *ngIf="(journals$ | async) || (issues$ | async)">
|
||||
<div class="relationships-item-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4" *ngIf="journals$ | async">
|
||||
<ds-related-items
|
||||
[items]="journals$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isJournalOfVolume'"
|
||||
[label]="'relationships.isSingleJournalOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="issues$ | async">
|
||||
<ds-related-items
|
||||
[items]="issues$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isIssueOfJournalVolume'"
|
||||
[label]="'relationships.isIssueOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -22,21 +22,21 @@
|
||||
<div class="col-12 col-md-6">
|
||||
|
||||
<ds-generic-item-page-field class="item-page-fields" [item]="object"
|
||||
[fields]="['journal.identifier.issn']"
|
||||
[fields]="['creativeworkseries.issn']"
|
||||
[label]="'journal.page.issn'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field class="item-page-fields" [item]="object"
|
||||
[fields]="['journal.publisher']"
|
||||
[fields]="['creativework.publisher']"
|
||||
[label]="'journal.page.publisher'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['journal.contributor.editor']"
|
||||
[fields]="['creativework.editor']"
|
||||
[label]="'journal.page.editor'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field class="item-page-fields" [item]="object"
|
||||
[fields]="['journal.identifier.description']"
|
||||
[fields]="['dc.description']"
|
||||
[label]="'journal.page.description'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
@@ -45,18 +45,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relationships-item-page" *ngIf="volumes$ | async">
|
||||
<div class="relationships-item-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4" *ngIf="volumes$ | async">
|
||||
<ds-related-items
|
||||
[items]="volumes$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="item"
|
||||
[relationType]="'isVolumeOfJournal'"
|
||||
[label]="'relationships.isVolumeOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="container search-container">
|
||||
<h3 class="h2">{{"item.page.journal.search.title" | translate}}</h3>
|
||||
|
@@ -18,30 +18,30 @@
|
||||
<h2 class="item-page-title-field">
|
||||
{{'orgunit.page.titleprefix' | translate}}
|
||||
<ds-metadata-values
|
||||
[mdValues]="object?.allMetadata(['orgunit.identifier.name'])"></ds-metadata-values>
|
||||
[mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['orgunit.identifier.city']"
|
||||
[fields]="['organization.address.addressLocality']"
|
||||
[label]="'orgunit.page.city'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['orgunit.identifier.country']"
|
||||
[fields]="['organization.adress.addressCountry']"
|
||||
[label]="'orgunit.page.country'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['orgunit.identifier.id']"
|
||||
[fields]="['dc.identifier']"
|
||||
[label]="'orgunit.page.id'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['orgunit.identifier.dateestablished']"
|
||||
[fields]="['organization.foundingDate']"
|
||||
[label]="'orgunit.page.dateestablished'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['orgunit.identifier.description']"
|
||||
[fields]="['dc.description']"
|
||||
[label]="'orgunit.page.description'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
@@ -50,27 +50,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relationships-item-page" *ngIf="(people$ | async) || (projects$ | async) || (publications$ | async)">
|
||||
<div class="relationships-item-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4" *ngIf="people$ | async">
|
||||
<ds-related-items
|
||||
[items]="people$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isPersonOfOrgUnit'"
|
||||
[label]="'relationships.isPersonOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="projects$ | async">
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isProjectOfOrgUnit'"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="publications$ | async">
|
||||
<ds-related-items
|
||||
[items]="publications$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isPublicationOfOrgUnit'"
|
||||
[label]="'relationships.isPublicationOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -17,38 +17,38 @@
|
||||
<div class="col-12 col-md-10">
|
||||
<h2 class="item-page-title-field">
|
||||
{{'person.page.titleprefix' | translate}}<ds-metadata-values
|
||||
[mdValues]="object?.allMetadata(['dc.contributor.author'])"></ds-metadata-values>
|
||||
[mdValues]="[object?.firstMetadata('person.familyName'), object?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['person.identifier.email']"
|
||||
[fields]="['person.email']"
|
||||
[label]="'person.page.email'">
|
||||
</ds-generic-item-page-field>
|
||||
<!-- <ds-generic-item-page-field [item]="object"-->
|
||||
<!-- [fields]="['person.identifier.orcid']"-->
|
||||
<!-- [label]="'person.page.orcid'">-->
|
||||
<!-- </ds-generic-item-page-field>-->
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['person.identifier.orcid']"
|
||||
[label]="'person.page.orcid'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['person.identifier.birthdate']"
|
||||
[fields]="['person.birthDate']"
|
||||
[label]="'person.page.birthdate'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['person.identifier.staffid']"
|
||||
[label]="'person.page.staffid'">
|
||||
</ds-generic-item-page-field>
|
||||
<!-- <ds-generic-item-page-field [item]="object"-->
|
||||
<!-- [fields]="['person.identifier.staffid']"-->
|
||||
<!-- [label]="'person.page.staffid'">-->
|
||||
<!-- </ds-generic-item-page-field>-->
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['person.identifier.jobtitle']"
|
||||
[fields]="['person.jobTitle']"
|
||||
[label]="'person.page.jobtitle'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['person.identifier.lastname']"
|
||||
[fields]="['person.familyName']"
|
||||
[label]="'person.page.lastname'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['person.identifier.firstname']"
|
||||
[fields]="['person.givenName']"
|
||||
[label]="'person.page.firstname'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
@@ -57,24 +57,24 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relationships-item-page" *ngIf="(projects$ | async) || (orgUnits$ | async)">
|
||||
<div class="relationships-item-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4" *ngIf="projects$ | async">
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isProjectOfPerson'"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="orgUnits$ | async">
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isOrgUnitOfPerson'"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="container search-container">
|
||||
<h3 class="h2">{{"item.page.person.search.title" | translate}}</h3>
|
||||
|
@@ -16,40 +16,39 @@
|
||||
</div>
|
||||
<div class="col-12 col-md-10">
|
||||
<h2 class="item-page-title-field">
|
||||
{{'project.page.titleprefix' | translate}}
|
||||
<ds-metadata-values
|
||||
[mdValues]="object?.allMetadata(['project.identifier.name'])"></ds-metadata-values>
|
||||
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-metadata-representation-list
|
||||
[label]="'project.page.contributor' | translate"
|
||||
[representations]="contributors$ | async">
|
||||
<ds-metadata-representation-list [parentItem]="object"
|
||||
[itemType]="'OrgUnit'"
|
||||
[metadataField]="'project.contributor.other'"
|
||||
[label]="'project.page.contributor' | translate">
|
||||
</ds-metadata-representation-list>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['project.identifier.funder']"
|
||||
[label]="'project.page.funder'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['project.identifier.status']"
|
||||
[label]="'project.page.status'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['project.identifier.expectedcompletion']"
|
||||
[label]="'project.page.expectedcompletion'">
|
||||
</ds-generic-item-page-field>
|
||||
<!-- <ds-generic-item-page-field [item]="object"-->
|
||||
<!-- [fields]="['project.identifier.status']"-->
|
||||
<!-- [label]="'project.page.status'">-->
|
||||
<!-- </ds-generic-item-page-field>-->
|
||||
<!-- <ds-generic-item-page-field [item]="object"-->
|
||||
<!-- [fields]="['project.identifier.expectedcompletion']"-->
|
||||
<!-- [label]="'project.page.expectedcompletion'">-->
|
||||
<!-- </ds-generic-item-page-field>-->
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['project.identifier.id']"
|
||||
[fields]="['dc.identifier']"
|
||||
[label]="'project.page.id'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['project.identifier.description']"
|
||||
[fields]="['dc.description']"
|
||||
[label]="'project.page.description'">
|
||||
</ds-generic-item-page-field>
|
||||
<ds-generic-item-page-field [item]="object"
|
||||
[fields]="['project.identifier.keyword']"
|
||||
[fields]="['dc.subject']"
|
||||
[label]="'project.page.keyword'">
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
@@ -58,28 +57,28 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relationships-item-page" *ngIf="(people$ | async) || (publications$ | async) || (orgUnits$ | async)">
|
||||
<div class="relationships-item-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4" *ngIf="people$ | async">
|
||||
<ds-related-items
|
||||
[items]="people$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isPersonOfProject'"
|
||||
[label]="'relationships.isPersonOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="publications$ | async">
|
||||
<ds-related-items
|
||||
[items]="publications$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isPublicationOfProject'"
|
||||
[label]="'relationships.isPublicationOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
<div class="col-12 col-md-4" *ngIf="orgUnits$ | async">
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
class="col-12 col-md-4"
|
||||
[parentItem]="object"
|
||||
[relationType]="'isOrgUnitOfProject'"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Reference in New Issue
Block a user