moved meta service into applicable page components

This commit is contained in:
William Welling
2017-10-13 13:09:00 -05:00
parent 617f3ad029
commit 6f42fa5af5
10 changed files with 108 additions and 181 deletions

View File

@@ -2,12 +2,11 @@ import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CollectionPageComponent } from './collection-page.component';
import { NormalizedCollection } from '../core/cache/models/normalized-collection.model';
@NgModule({
imports: [
RouterModule.forChild([
{ path: ':id', component: CollectionPageComponent, pathMatch: 'full', data: { type: NormalizedCollection } }
{ path: ':id', component: CollectionPageComponent, pathMatch: 'full' }
])
]
})

View File

@@ -7,6 +7,8 @@ import {
} from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { PageInfo } from '../core/shared/page-info.model';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { Collection } from '../core/shared/collection.model';
@@ -18,8 +20,8 @@ import { Item } from '../core/shared/item.model';
import { SortOptions, SortDirection } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { hasValue, isNotEmpty, isUndefined } from '../shared/empty.util';
import { PageInfo } from '../core/shared/page-info.model';
import { Observable } from 'rxjs/Observable';
import { MetadataService } from '../core/metadata/metadata.service';
import { fadeIn, fadeInOut } from '../shared/animations/fade';
@@ -41,9 +43,12 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
private subs: Subscription[] = [];
private collectionId: string;
constructor(private collectionDataService: CollectionDataService,
private itemDataService: ItemDataService,
private route: ActivatedRoute) {
constructor(
private collectionDataService: CollectionDataService,
private itemDataService: ItemDataService,
private metadata: MetadataService,
private route: ActivatedRoute
) {
this.paginationConfig = new PaginationComponentOptions();
this.paginationConfig.id = 'collection-page-pagination';
this.paginationConfig.pageSizeOptions = [4];
@@ -57,12 +62,13 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
Observable.combineLatest(
this.route.params,
this.route.queryParams,
(params, queryParams,) => {
(params, queryParams, ) => {
return Object.assign({}, params, queryParams);
})
.subscribe((params) => {
this.collectionId = params.id;
this.collectionData = this.collectionDataService.findById(this.collectionId);
this.metadata.processRemoteData(this.collectionData);
this.subs.push(this.collectionData.payload.subscribe((collection) => this.logoData = collection.logo));
const page = +params.page || this.paginationConfig.currentPage;

View File

@@ -2,12 +2,11 @@ import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommunityPageComponent } from './community-page.component';
import { NormalizedCommunity } from '../core/cache/models/normalized-community.model';
@NgModule({
imports: [
RouterModule.forChild([
{ path: ':id', component: CommunityPageComponent, pathMatch: 'full', data: { type: NormalizedCommunity } }
{ path: ':id', component: CommunityPageComponent, pathMatch: 'full' }
])
]
})

View File

@@ -9,6 +9,8 @@ import { RemoteData } from '../core/data/remote-data';
import { CommunityDataService } from '../core/data/community-data.service';
import { hasValue } from '../shared/empty.util';
import { MetadataService } from '../core/metadata/metadata.service';
import { fadeInOut } from '../shared/animations/fade';
@Component({
@@ -24,6 +26,7 @@ export class CommunityPageComponent implements OnInit, OnDestroy {
constructor(
private communityDataService: CommunityDataService,
private metadata: MetadataService,
private route: ActivatedRoute
) {
@@ -32,15 +35,13 @@ export class CommunityPageComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.route.params.subscribe((params: Params) => {
this.communityData = this.communityDataService.findById(params.id);
this.subs.push(this.communityData.payload
.subscribe((community) => this.logoData = community.logo));
this.metadata.processRemoteData(this.communityData);
this.subs.push(this.communityData.payload.subscribe((community) => this.logoData = community.logo));
});
}
ngOnDestroy(): void {
this.subs
.filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe());
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
}
}

View File

@@ -1,15 +1,17 @@
import { Component, OnInit } from '@angular/core';
import { animate, state, transition, trigger, style, keyframes } from '@angular/animations';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { ItemPageComponent } from '../simple/item-page.component';
import { Metadatum } from '../../core/shared/metadatum.model';
import { ItemDataService } from '../../core/data/item-data.service';
import { ActivatedRoute } from '@angular/router';
import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model';
import { MetadataService } from '../../core/metadata/metadata.service';
import { fadeInOut } from '../../shared/animations/fade';
/**
@@ -30,8 +32,8 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit {
metadata: Observable<Metadatum[]>;
constructor(route: ActivatedRoute, items: ItemDataService) {
super(route, items);
constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) {
super(route, items, metadataService);
}
/*** AoT inheritance fix, will hopefully be resolved in the near future **/

View File

@@ -3,13 +3,12 @@ import { RouterModule } from '@angular/router';
import { ItemPageComponent } from './simple/item-page.component';
import { FullItemPageComponent } from './full/full-item-page.component';
import { NormalizedItem } from '../core/cache/models/normalized-item.model';
@NgModule({
imports: [
RouterModule.forChild([
{ path: ':id', component: ItemPageComponent, pathMatch: 'full', data: { type: NormalizedItem } },
{ path: ':id/full', component: FullItemPageComponent, data: { type: NormalizedItem } }
{ path: ':id', component: ItemPageComponent, pathMatch: 'full' },
{ path: ':id/full', component: FullItemPageComponent }
])
]
})

View File

@@ -8,6 +8,8 @@ import { ItemDataService } from '../../core/data/item-data.service';
import { RemoteData } from '../../core/data/remote-data';
import { Bitstream } from '../../core/shared/bitstream.model';
import { MetadataService } from '../../core/metadata/metadata.service';
import { fadeInOut } from '../../shared/animations/fade';
/**
@@ -31,7 +33,11 @@ export class ItemPageComponent implements OnInit {
thumbnail: Observable<Bitstream>;
constructor(private route: ActivatedRoute, private items: ItemDataService) {
constructor(
private route: ActivatedRoute,
private items: ItemDataService,
private metadataService: MetadataService
) {
}
@@ -44,6 +50,7 @@ export class ItemPageComponent implements OnInit {
initialize(params) {
this.id = +params.id;
this.item = this.items.findById(params.id);
this.metadataService.processRemoteData(this.item);
this.thumbnail = this.item.payload.flatMap((i) => i.getThumbnail());
}

View File

@@ -4,7 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing';
import { Location, CommonModule } from '@angular/common';
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { By, Meta, MetaDefinition, Title } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
@@ -19,17 +19,16 @@ import { CoreState } from '../core.reducers';
import { GlobalConfig } from '../../../config/global-config.interface';
import { ENV_CONFIG, GLOBAL_CONFIG } from '../../../config';
import { ItemDataService } from '../data/item-data.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { RequestService } from '../data/request.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model';
import { NormalizedItem } from '../cache/models/normalized-item.model';
import { MockItem } from '../../shared/mocks/mock-item';
import { MockNormalizedItem } from '../../shared/mocks/mock-normalized-item';
import { MockRouter } from '../../shared/mocks/mock-router';
import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
/* tslint:disable:max-classes-per-file */
@@ -42,7 +41,13 @@ class TestComponent {
}
}
@Component({ template: '' }) class DummyItemComponent { }
@Component({ template: '' }) class DummyItemComponent {
constructor(private route: ActivatedRoute, private items: ItemDataService, private metadata: MetadataService) {
this.route.params.subscribe((params) => {
this.metadata.processRemoteData(this.items.findById(params.id));
});
}
}
/* tslint:enable:max-classes-per-file */
describe('MetadataService', () => {
@@ -58,6 +63,7 @@ describe('MetadataService', () => {
let responseCacheService: ResponseCacheService;
let requestService: RequestService;
let remoteDataBuildService: RemoteDataBuildService;
let itemDataService: ItemDataService;
let location: Location;
let router: Router;
@@ -88,8 +94,8 @@ describe('MetadataService', () => {
}
}),
RouterTestingModule.withRoutes([
{ path: 'items/:id', component: DummyItemComponent, pathMatch: 'full', data: { type: NormalizedItem } },
{ path: 'other', component: DummyItemComponent, pathMatch: 'full', data: { title: 'Dummy Title', description: 'This is a dummy component for testing!' } }
{ path: 'items/:id', component: DummyItemComponent, pathMatch: 'full' },
{ path: 'other', component: DummyItemComponent, pathMatch: 'full', data: { title: 'Dummy Title', description: 'This is a dummy item component for testing!' } }
])
],
declarations: [
@@ -104,12 +110,14 @@ describe('MetadataService', () => {
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },
Meta,
Title,
ItemDataService,
MetadataService
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
meta = TestBed.get(Meta);
title = TestBed.get(Title);
itemDataService = TestBed.get(ItemDataService);
metadataService = TestBed.get(MetadataService);
envConfig = TestBed.get(GLOBAL_CONFIG);
@@ -122,14 +130,8 @@ describe('MetadataService', () => {
tagStore = metadataService.getTagStore();
});
beforeEach(() => {
spyOn(objectCacheService, 'getByUUID').and.returnValue(Observable.create((observer) => {
observer.next(MockNormalizedItem);
}));
});
it('items page should set meta tags', fakeAsync(() => {
spyOn(remoteDataBuildService, 'build').and.returnValue(MockItem);
spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(MockItem));
router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']);
tick();
expect(title.getTitle()).toEqual('Test PowerPoint Document');
@@ -142,7 +144,7 @@ describe('MetadataService', () => {
}));
it('items page should set meta tags as published Thesis', fakeAsync(() => {
spyOn(remoteDataBuildService, 'build').and.returnValue(mockPublisher(mockType(MockItem, 'Thesis')));
spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockPublisher(mockType(MockItem, 'Thesis'))));
router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']);
tick();
expect(tagStore.get('citation_dissertation_name')[0].content).toEqual('Test PowerPoint Document');
@@ -152,14 +154,14 @@ describe('MetadataService', () => {
}));
it('items page should set meta tags as published Technical Report', fakeAsync(() => {
spyOn(remoteDataBuildService, 'build').and.returnValue(mockPublisher(mockType(MockItem, 'Technical Report')));
spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(mockPublisher(mockType(MockItem, 'Technical Report'))));
router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']);
tick();
expect(tagStore.get('citation_technical_report_institution')[0].content).toEqual('Mock Publisher');
}));
it('other navigation should title and description', fakeAsync(() => {
spyOn(remoteDataBuildService, 'build').and.returnValue(MockItem);
spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(MockItem));
router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']);
tick();
expect(tagStore.size).toBeGreaterThan(0)
@@ -168,9 +170,38 @@ describe('MetadataService', () => {
expect(tagStore.size).toEqual(2);
expect(title.getTitle()).toEqual('Dummy Title');
expect(tagStore.get('title')[0].content).toEqual('Dummy Title');
expect(tagStore.get('description')[0].content).toEqual('This is a dummy component for testing!');
expect(tagStore.get('description')[0].content).toEqual('This is a dummy item component for testing!');
}));
const mockRemoteData = (mockItem: Item): RemoteData<Item> => {
return new RemoteData<Item>(
Observable.create((observer) => {
observer.next('');
}),
Observable.create((observer) => {
observer.next(false);
}),
Observable.create((observer) => {
observer.next(false);
}),
Observable.create((observer) => {
observer.next(true);
}),
Observable.create((observer) => {
observer.next('');
}),
Observable.create((observer) => {
observer.next(200);
}),
Observable.create((observer) => {
observer.next({});
}),
Observable.create((observer) => {
observer.next(MockItem);
})
);
}
const mockType = (mockItem: Item, type: string): Item => {
const typedMockItem = Object.assign(new Item(), mockItem) as Item;
for (const metadatum of typedMockItem.metadata) {

View File

@@ -17,13 +17,12 @@ import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { RemoteData } from '../data/remote-data';
import { Bitstream } from '../shared/bitstream.model';
import { CacheableObject } from '../cache/object-cache.reducer';
import { DSpaceObject } from '../shared/dspace-object.model';
import { Item } from '../shared/item.model';
import { Metadatum } from '../shared/metadatum.model';
import { ObjectCacheService } from '../cache/object-cache.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
@@ -38,8 +37,6 @@ export class MetadataService {
constructor(
private router: Router,
private objectCacheService: ObjectCacheService,
private remoteDataBuildService: RemoteDataBuildService,
private translate: TranslateService,
private meta: Meta,
private title: Title,
@@ -67,29 +64,29 @@ export class MetadataService {
});
}
public processRemoteData(remoteData: RemoteData<CacheableObject>): void {
remoteData.payload.take(1).subscribe((dspaceObject: DSpaceObject) => {
if (!this.initialized) {
this.initialize(dspaceObject);
}
this.currentObject.next(dspaceObject);
});
}
private processRouteChange(routeInfo: any): void {
if (routeInfo.params.value.id && routeInfo.data.value.type) {
this.objectCacheService.getByUUID(routeInfo.params.value.id, routeInfo.data.value.type)
.first().subscribe((normalizedObject: CacheableObject) => {
const dspaceObject = this.remoteDataBuildService.build(normalizedObject) as DSpaceObject;
if (!this.initialized) {
this.initialize(dspaceObject);
}
this.currentObject.next(dspaceObject);
});
} else {
if (routeInfo.params.value.id === undefined) {
this.clearMetaTags();
if (routeInfo.data.value.title) {
this.translate.get(routeInfo.data.value.title).take(1).subscribe((translatedTitle: string) => {
this.addMetaTag('title', translatedTitle);
this.title.setTitle(translatedTitle);
});
}
if (routeInfo.data.value.description) {
this.translate.get(routeInfo.data.value.description).take(1).subscribe((translatedDescription: string) => {
this.addMetaTag('description', translatedDescription);
});
}
}
if (routeInfo.data.value.title) {
this.translate.get(routeInfo.data.value.title).take(1).subscribe((translatedTitle: string) => {
this.addMetaTag('title', translatedTitle);
this.title.setTitle(translatedTitle);
});
}
if (routeInfo.data.value.description) {
this.translate.get(routeInfo.data.value.description).take(1).subscribe((translatedDescription: string) => {
this.addMetaTag('description', translatedDescription);
});
}
}

View File

@@ -1,114 +0,0 @@
import { NormalizedItem } from '../../core/cache/models/normalized-item.model';
export const MockNormalizedItem: NormalizedItem = Object.assign(new NormalizedItem(), {
handle: '10673/6',
lastModified: new Date('2017-04-24T19:44:08.178+0000'),
isArchived: true,
isDiscoverable: true,
isWithdrawn: false,
bitstreams: [
'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/a7cd7d97-4e40-41db-80a8-fac908b63bb8',
'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/9ff3df0d-1709-472f-8c00-d3e8db2153c8',
'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/d660a4b8-e7cc-45cd-b026-35f98c5bd3ba'
],
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357',
id: '0ec7ff22-f211-40ab-a69e-c819b0b1f357',
uuid: '0ec7ff22-f211-40ab-a69e-c819b0b1f357',
type: 'item',
name: 'Test PowerPoint Document',
metadata: [
{
key: 'dc.creator',
language: 'en_US',
value: 'Doe, Jane L'
},
{
key: 'dc.date.accessioned',
language: null,
value: '1650-06-26T19:58:25Z'
},
{
key: 'dc.date.available',
language: null,
value: '1650-06-26T19:58:25Z'
},
{
key: 'dc.date.issued',
language: null,
value: '1650-06-26'
},
{
key: 'dc.identifier.issn',
language: 'en_US',
value: '123456789'
},
{
key: 'dc.identifier.uri',
language: null,
value: 'http://dspace7.4science.it/xmlui/handle/10673/6'
},
{
key: 'dc.description.abstract',
language: 'en_US',
value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!'
},
{
key: 'dc.description.provenance',
language: 'en',
value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)'
},
{
key: 'dc.description.provenance',
language: 'en',
value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).'
},
{
key: 'dc.description.provenance',
language: 'en',
value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).'
},
{
key: 'dc.description.provenance',
language: 'en',
value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).'
},
{
key: 'dc.language',
language: 'en_US',
value: 'en'
},
{
key: 'dc.rights',
language: 'en_US',
value: '© Jane Doe'
},
{
key: 'dc.subject',
language: 'en_US',
value: 'keyword1'
},
{
key: 'dc.subject',
language: 'en_US',
value: 'keyword2'
},
{
key: 'dc.subject',
language: 'en_US',
value: 'keyword3'
},
{
key: 'dc.title',
language: 'en_US',
value: 'Test PowerPoint Document'
},
{
key: 'dc.type',
language: 'en_US',
value: 'text'
}
],
owningCollection: [
'https://dspace7.4science.it/dspace-spring-rest/api/core/collections/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb'
]
})