mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-09 11:03:05 +00:00
Merge branch 'master' into browse-by-features
Conflicts: src/app/core/shared/operators.ts
This commit is contained in:
@@ -2,12 +2,23 @@ import { NgModule } from '@angular/core';
|
|||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
import { CollectionPageComponent } from './collection-page.component';
|
import { CollectionPageComponent } from './collection-page.component';
|
||||||
|
import { CollectionPageResolver } from './collection-page.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{ path: ':id', component: CollectionPageComponent, pathMatch: 'full' }
|
{
|
||||||
|
path: ':id',
|
||||||
|
component: CollectionPageComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
collection: CollectionPageResolver
|
||||||
|
}
|
||||||
|
}
|
||||||
])
|
])
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CollectionPageResolver,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CollectionPageRoutingModule {
|
export class CollectionPageRoutingModule {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="collection-page"
|
<div class="collection-page"
|
||||||
*ngVar="(collectionRDObs | async) as collectionRD">
|
*ngVar="(collectionRD$ | async) as collectionRD">
|
||||||
<div *ngIf="collectionRD?.hasSucceeded" @fadeInOut>
|
<div *ngIf="collectionRD?.hasSucceeded" @fadeInOut>
|
||||||
<div *ngIf="collectionRD?.payload as collection">
|
<div *ngIf="collectionRD?.payload as collection">
|
||||||
<!-- Collection Name -->
|
<!-- Collection Name -->
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
[name]="collection.name">
|
[name]="collection.name">
|
||||||
</ds-comcol-page-header>
|
</ds-comcol-page-header>
|
||||||
<!-- Collection logo -->
|
<!-- Collection logo -->
|
||||||
<ds-comcol-page-logo *ngIf="logoRDObs"
|
<ds-comcol-page-logo *ngIf="logoRD$"
|
||||||
[logo]="(logoRDObs | async)?.payload"
|
[logo]="(logoRD$ | async)?.payload"
|
||||||
[alternateText]="'Collection Logo'">
|
[alternateText]="'Collection Logo'">
|
||||||
</ds-comcol-page-logo>
|
</ds-comcol-page-logo>
|
||||||
<!-- Introductionary text -->
|
<!-- Introductionary text -->
|
||||||
@@ -38,14 +38,14 @@
|
|||||||
<ds-error *ngIf="collectionRD?.hasFailed" message="{{'error.collection' | translate}}"></ds-error>
|
<ds-error *ngIf="collectionRD?.hasFailed" message="{{'error.collection' | translate}}"></ds-error>
|
||||||
<ds-loading *ngIf="collectionRD?.isLoading" message="{{'loading.collection' | translate}}"></ds-loading>
|
<ds-loading *ngIf="collectionRD?.isLoading" message="{{'loading.collection' | translate}}"></ds-loading>
|
||||||
<br>
|
<br>
|
||||||
<ng-container *ngVar="(itemRDObs | async) as itemRD">
|
<ng-container *ngVar="(itemRD$ | async) as itemRD">
|
||||||
<div *ngIf="itemRD?.hasSucceeded" @fadeIn>
|
<div *ngIf="itemRD?.hasSucceeded" @fadeIn>
|
||||||
<h2>{{'collection.page.browse.recent.head' | translate}}</h2>
|
<h2>{{'collection.page.browse.recent.head' | translate}}</h2>
|
||||||
<ds-viewable-collection
|
<ds-viewable-collection
|
||||||
[config]="paginationConfig"
|
[config]="paginationConfig"
|
||||||
[sortConfig]="sortConfig"
|
[sortConfig]="sortConfig"
|
||||||
[objects]="itemRD"
|
[objects]="itemRD"
|
||||||
[hideGear]="false"
|
[hideGear]="true"
|
||||||
(paginationChange)="onPaginationChange($event)">
|
(paginationChange)="onPaginationChange($event)">
|
||||||
</ds-viewable-collection>
|
</ds-viewable-collection>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -5,7 +5,6 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
import { ItemDataService } from '../core/data/item-data.service';
|
|
||||||
import { PaginatedList } from '../core/data/paginated-list';
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
|
|
||||||
@@ -18,6 +17,11 @@ import { Item } from '../core/shared/item.model';
|
|||||||
import { fadeIn, fadeInOut } from '../shared/animations/fade';
|
import { fadeIn, fadeInOut } from '../shared/animations/fade';
|
||||||
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
|
import { filter, flatMap, map } from 'rxjs/operators';
|
||||||
|
import { SearchService } from '../+search-page/search-service/search.service';
|
||||||
|
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||||
|
import { toDSpaceObjectListRD } from '../core/shared/operators';
|
||||||
|
import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-page',
|
selector: 'ds-collection-page',
|
||||||
@@ -30,9 +34,9 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CollectionPageComponent implements OnInit, OnDestroy {
|
export class CollectionPageComponent implements OnInit, OnDestroy {
|
||||||
collectionRDObs: Observable<RemoteData<Collection>>;
|
collectionRD$: Observable<RemoteData<Collection>>;
|
||||||
itemRDObs: Observable<RemoteData<PaginatedList<Item>>>;
|
itemRD$: Observable<RemoteData<PaginatedList<Item>>>;
|
||||||
logoRDObs: Observable<RemoteData<Bitstream>>;
|
logoRD$: Observable<RemoteData<Bitstream>>;
|
||||||
paginationConfig: PaginationComponentOptions;
|
paginationConfig: PaginationComponentOptions;
|
||||||
sortConfig: SortOptions;
|
sortConfig: SortOptions;
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
@@ -40,7 +44,7 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private collectionDataService: CollectionDataService,
|
private collectionDataService: CollectionDataService,
|
||||||
private itemDataService: ItemDataService,
|
private searchService: SearchService,
|
||||||
private metadata: MetadataService,
|
private metadata: MetadataService,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute
|
||||||
) {
|
) {
|
||||||
@@ -48,52 +52,41 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
|
|||||||
this.paginationConfig.id = 'collection-page-pagination';
|
this.paginationConfig.id = 'collection-page-pagination';
|
||||||
this.paginationConfig.pageSize = 5;
|
this.paginationConfig.pageSize = 5;
|
||||||
this.paginationConfig.currentPage = 1;
|
this.paginationConfig.currentPage = 1;
|
||||||
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
this.sortConfig = new SortOptions('dc.date.accessioned', SortDirection.DESC);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.collectionRD$ = this.route.data.map((data) => data.collection);
|
||||||
|
this.logoRD$ = this.collectionRD$.pipe(
|
||||||
|
map((rd: RemoteData<Collection>) => rd.payload),
|
||||||
|
filter((collection: Collection) => hasValue(collection)),
|
||||||
|
flatMap((collection: Collection) => collection.logo)
|
||||||
|
);
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
Observable.combineLatest(
|
this.route.queryParams.subscribe((params) => {
|
||||||
this.route.params,
|
this.metadata.processRemoteData(this.collectionRD$);
|
||||||
this.route.queryParams,
|
const page = +params.page || this.paginationConfig.currentPage;
|
||||||
(params, queryParams, ) => {
|
const pageSize = +params.pageSize || this.paginationConfig.pageSize;
|
||||||
return Object.assign({}, params, queryParams);
|
const pagination = Object.assign({},
|
||||||
})
|
this.paginationConfig,
|
||||||
.subscribe((params) => {
|
{ currentPage: page, pageSize: pageSize }
|
||||||
this.collectionId = params.id;
|
);
|
||||||
this.collectionRDObs = this.collectionDataService.findById(this.collectionId);
|
this.updatePage({
|
||||||
this.metadata.processRemoteData(this.collectionRDObs);
|
pagination: pagination,
|
||||||
this.subs.push(this.collectionRDObs
|
sort: this.sortConfig
|
||||||
.map((rd: RemoteData<Collection>) => rd.payload)
|
});
|
||||||
.filter((collection: Collection) => hasValue(collection))
|
}));
|
||||||
.subscribe((collection: Collection) => this.logoRDObs = collection.logo));
|
|
||||||
|
|
||||||
const page = +params.page || this.paginationConfig.currentPage;
|
|
||||||
const pageSize = +params.pageSize || this.paginationConfig.pageSize;
|
|
||||||
const sortDirection = +params.page || this.sortConfig.direction;
|
|
||||||
const pagination = Object.assign({},
|
|
||||||
this.paginationConfig,
|
|
||||||
{ currentPage: page, pageSize: pageSize }
|
|
||||||
);
|
|
||||||
const sort = Object.assign({},
|
|
||||||
this.sortConfig,
|
|
||||||
{ direction: sortDirection, field: params.sortField }
|
|
||||||
);
|
|
||||||
this.updatePage({
|
|
||||||
pagination: pagination,
|
|
||||||
sort: sort
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePage(searchOptions) {
|
updatePage(searchOptions) {
|
||||||
this.itemRDObs = this.itemDataService.findAll({
|
this.itemRD$ = this.searchService.search(
|
||||||
scopeID: this.collectionId,
|
new PaginatedSearchOptions({
|
||||||
currentPage: searchOptions.pagination.currentPage,
|
scope: this.collectionId,
|
||||||
elementsPerPage: searchOptions.pagination.pageSize,
|
pagination: searchOptions.pagination,
|
||||||
sort: searchOptions.sort
|
sort: searchOptions.sort,
|
||||||
});
|
dsoType: DSpaceObjectType.ITEM
|
||||||
|
})).pipe(toDSpaceObjectListRD()) as Observable<RemoteData<PaginatedList<Item>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
@@ -5,11 +5,13 @@ import { SharedModule } from '../shared/shared.module';
|
|||||||
|
|
||||||
import { CollectionPageComponent } from './collection-page.component';
|
import { CollectionPageComponent } from './collection-page.component';
|
||||||
import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
||||||
|
import { SearchPageModule } from '../+search-page/search-page.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
SearchPageModule,
|
||||||
CollectionPageRoutingModule
|
CollectionPageRoutingModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
28
src/app/+collection-page/collection-page.resolver.ts
Normal file
28
src/app/+collection-page/collection-page.resolver.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { Collection } from '../core/shared/collection.model';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
|
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that requests a specific collection before the route is activated
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CollectionPageResolver implements Resolve<RemoteData<Collection>> {
|
||||||
|
constructor(private collectionService: CollectionDataService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for resolving a collection based on the parameters in the current route
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns Observable<<RemoteData<Collection>> Emits the found collection based on the parameters in the current route
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> {
|
||||||
|
return this.collectionService.findById(route.params.id).pipe(
|
||||||
|
getSucceededRemoteData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -2,12 +2,23 @@ import { NgModule } from '@angular/core';
|
|||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
import { CommunityPageComponent } from './community-page.component';
|
import { CommunityPageComponent } from './community-page.component';
|
||||||
|
import { CommunityPageResolver } from './community-page.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{ path: ':id', component: CommunityPageComponent, pathMatch: 'full' }
|
{
|
||||||
|
path: ':id',
|
||||||
|
component: CommunityPageComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
community: CommunityPageResolver
|
||||||
|
}
|
||||||
|
}
|
||||||
])
|
])
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CommunityPageResolver,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CommunityPageRoutingModule {
|
export class CommunityPageRoutingModule {
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
<div class="container" *ngVar="(communityRDObs | async) as communityRD">
|
<div class="container" *ngVar="(communityRD$ | async) as communityRD">
|
||||||
<div class="community-page" *ngIf="communityRD?.hasSucceeded" @fadeInOut>
|
<div class="community-page" *ngIf="communityRD?.hasSucceeded" @fadeInOut>
|
||||||
<div *ngIf="communityRD?.payload; let communityPayload">
|
<div *ngIf="communityRD?.payload; let communityPayload">
|
||||||
<!-- Community name -->
|
<!-- Community name -->
|
||||||
<ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
|
<ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
|
||||||
<!-- Community logo -->
|
<!-- Community logo -->
|
||||||
<ds-comcol-page-logo *ngIf="logoRDObs"
|
<ds-comcol-page-logo *ngIf="logoRD$"
|
||||||
[logo]="(logoRDObs | async)?.payload"
|
[logo]="(logoRD$ | async)?.payload"
|
||||||
[alternateText]="'Community Logo'">
|
[alternateText]="'Community Logo'">
|
||||||
</ds-comcol-page-logo>
|
</ds-comcol-page-logo>
|
||||||
<!-- Introductory text -->
|
<!-- Introductory text -->
|
||||||
|
@@ -22,8 +22,8 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
animations: [fadeInOut]
|
animations: [fadeInOut]
|
||||||
})
|
})
|
||||||
export class CommunityPageComponent implements OnInit, OnDestroy {
|
export class CommunityPageComponent implements OnInit, OnDestroy {
|
||||||
communityRDObs: Observable<RemoteData<Community>>;
|
communityRD$: Observable<RemoteData<Community>>;
|
||||||
logoRDObs: Observable<RemoteData<Bitstream>>;
|
logoRD$: Observable<RemoteData<Bitstream>>;
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -35,14 +35,11 @@ export class CommunityPageComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.params.subscribe((params: Params) => {
|
this.communityRD$ = this.route.data.map((data) => data.community);
|
||||||
this.communityRDObs = this.communityDataService.findById(params.id);
|
this.logoRD$ = this.communityRD$
|
||||||
this.metadata.processRemoteData(this.communityRDObs);
|
.map((rd: RemoteData<Community>) => rd.payload)
|
||||||
this.subs.push(this.communityRDObs
|
.filter((community: Community) => hasValue(community))
|
||||||
.map((rd: RemoteData<Community>) => rd.payload)
|
.flatMap((community: Community) => community.logo);
|
||||||
.filter((community: Community) => hasValue(community))
|
|
||||||
.subscribe((community: Community) => this.logoRDObs = community.logo));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
28
src/app/+community-page/community-page.resolver.ts
Normal file
28
src/app/+community-page/community-page.resolver.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
|
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||||
|
import { Community } from '../core/shared/community.model';
|
||||||
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that requests a specific community before the route is activated
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CommunityPageResolver implements Resolve<RemoteData<Community>> {
|
||||||
|
constructor(private communityService: CommunityDataService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for resolving a community based on the parameters in the current route
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns Observable<<RemoteData<Community>> Emits the found community based on the parameters in the current route
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> {
|
||||||
|
return this.communityService.findById(route.params.id).pipe(
|
||||||
|
getSucceededRemoteData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,15 @@
|
|||||||
<div class="container" *ngVar="(itemRDObs | async) as itemRD">
|
<div class="container" *ngVar="(itemRD$ | async) as itemRD">
|
||||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
||||||
<div *ngIf="itemRD?.payload as item">
|
<div *ngIf="itemRD?.payload as item">
|
||||||
<ds-item-page-title-field [item]="item"></ds-item-page-title-field>
|
<ds-item-page-title-field [item]="item"></ds-item-page-title-field>
|
||||||
<div class="simple-view-link">
|
<div class="simple-view-link my-3">
|
||||||
<a class="btn btn-outline-primary col-4" [routerLink]="['/items/' + item.id]">
|
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id]">
|
||||||
{{"item.page.link.simple" | translate}}
|
{{"item.page.link.simple" | translate}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-responsive table-striped">
|
<table class="table table-responsive table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let metadatum of (metadataObs | async)">
|
<tr *ngFor="let metadatum of (metadata$ | async)">
|
||||||
<td>{{metadatum.key}}</td>
|
<td>{{metadatum.key}}</td>
|
||||||
<td>{{metadatum.value}}</td>
|
<td>{{metadatum.value}}</td>
|
||||||
<td>{{metadatum.language}}</td>
|
<td>{{metadatum.language}}</td>
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
@import '../../../styles/variables.scss';
|
@import '../../../styles/variables.scss';
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
div.simple-view-link {
|
div.simple-view-link {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 20px;
|
a {
|
||||||
|
min-width: 25%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -30,9 +30,9 @@ import { hasValue } from '../../shared/empty.util';
|
|||||||
})
|
})
|
||||||
export class FullItemPageComponent extends ItemPageComponent implements OnInit {
|
export class FullItemPageComponent extends ItemPageComponent implements OnInit {
|
||||||
|
|
||||||
itemRDObs: Observable<RemoteData<Item>>;
|
itemRD$: Observable<RemoteData<Item>>;
|
||||||
|
|
||||||
metadataObs: Observable<Metadatum[]>;
|
metadata$: Observable<Metadatum[]>;
|
||||||
|
|
||||||
constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) {
|
constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) {
|
||||||
super(route, items, metadataService);
|
super(route, items, metadataService);
|
||||||
@@ -41,14 +41,9 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit {
|
|||||||
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
|
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
this.metadata$ = this.itemRD$
|
||||||
|
|
||||||
initialize(params) {
|
|
||||||
super.initialize(params);
|
|
||||||
this.metadataObs = this.itemRDObs
|
|
||||||
.map((rd: RemoteData<Item>) => rd.payload)
|
.map((rd: RemoteData<Item>) => rd.payload)
|
||||||
.filter((item: Item) => hasValue(item))
|
.filter((item: Item) => hasValue(item))
|
||||||
.map((item: Item) => item.metadata);
|
.map((item: Item) => item.metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,13 +3,30 @@ import { RouterModule } from '@angular/router';
|
|||||||
|
|
||||||
import { ItemPageComponent } from './simple/item-page.component';
|
import { ItemPageComponent } from './simple/item-page.component';
|
||||||
import { FullItemPageComponent } from './full/full-item-page.component';
|
import { FullItemPageComponent } from './full/full-item-page.component';
|
||||||
|
import { ItemPageResolver } from './item-page.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{ path: ':id', component: ItemPageComponent, pathMatch: 'full' },
|
{
|
||||||
{ path: ':id/full', component: FullItemPageComponent }
|
path: ':id',
|
||||||
|
component: ItemPageComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
item: ItemPageResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':id/full',
|
||||||
|
component: FullItemPageComponent,
|
||||||
|
resolve: {
|
||||||
|
item: ItemPageResolver
|
||||||
|
}
|
||||||
|
}
|
||||||
])
|
])
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ItemPageResolver,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ItemPageRoutingModule {
|
export class ItemPageRoutingModule {
|
||||||
|
28
src/app/+item-page/item-page.resolver.ts
Normal file
28
src/app/+item-page/item-page.resolver.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
|
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||||
|
import { ItemDataService } from '../core/data/item-data.service';
|
||||||
|
import { Item } from '../core/shared/item.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that requests a specific item before the route is activated
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class ItemPageResolver implements Resolve<RemoteData<Item>> {
|
||||||
|
constructor(private itemService: ItemDataService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for resolving an item based on the parameters in the current route
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
|
||||||
|
return this.itemService.findById(route.params.id).pipe(
|
||||||
|
getSucceededRemoteData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +1,11 @@
|
|||||||
<div class="container" *ngVar="(itemRDObs | async) as itemRD">
|
<div class="container" *ngVar="(itemRD$ | async) as itemRD">
|
||||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
||||||
<div *ngIf="itemRD?.payload as item">
|
<div *ngIf="itemRD?.payload as item">
|
||||||
<ds-item-page-title-field [item]="item"></ds-item-page-title-field>
|
<ds-item-page-title-field [item]="item"></ds-item-page-title-field>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="thumbnailObs | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-item-page-file-section [item]="item"></ds-item-page-file-section>
|
<ds-item-page-file-section [item]="item"></ds-item-page-file-section>
|
||||||
<ds-item-page-date-field [item]="item"></ds-item-page-date-field>
|
<ds-item-page-date-field [item]="item"></ds-item-page-date-field>
|
||||||
|
@@ -31,9 +31,9 @@ export class ItemPageComponent implements OnInit {
|
|||||||
|
|
||||||
private sub: any;
|
private sub: any;
|
||||||
|
|
||||||
itemRDObs: Observable<RemoteData<Item>>;
|
itemRD$: Observable<RemoteData<Item>>;
|
||||||
|
|
||||||
thumbnailObs: Observable<Bitstream>;
|
thumbnail$: Observable<Bitstream>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@@ -44,19 +44,11 @@ export class ItemPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.sub = this.route.params.subscribe((params) => {
|
this.itemRD$ = this.route.data.map((data) => data.item);
|
||||||
this.initialize(params);
|
this.metadataService.processRemoteData(this.itemRD$);
|
||||||
});
|
this.thumbnail$ = this.itemRD$
|
||||||
}
|
|
||||||
|
|
||||||
initialize(params) {
|
|
||||||
this.id = +params.id;
|
|
||||||
this.itemRDObs = this.items.findById(params.id);
|
|
||||||
this.metadataService.processRemoteData(this.itemRDObs);
|
|
||||||
this.thumbnailObs = this.itemRDObs
|
|
||||||
.map((rd: RemoteData<Item>) => rd.payload)
|
.map((rd: RemoteData<Item>) => rd.payload)
|
||||||
.filter((item: Item) => hasValue(item))
|
.filter((item: Item) => hasValue(item))
|
||||||
.flatMap((item: Item) => item.getThumbnail());
|
.flatMap((item: Item) => item.getThumbnail());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,22 +2,19 @@ import 'rxjs/add/observable/of';
|
|||||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||||
|
import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
|
||||||
|
import { SearchFilter } from './search-filter.model';
|
||||||
|
|
||||||
describe('PaginatedSearchOptions', () => {
|
describe('PaginatedSearchOptions', () => {
|
||||||
let options: PaginatedSearchOptions;
|
let options: PaginatedSearchOptions;
|
||||||
const sortOptions = new SortOptions('test.field', SortDirection.DESC);
|
const sortOptions = new SortOptions('test.field', SortDirection.DESC);
|
||||||
const pageOptions = Object.assign(new PaginationComponentOptions(), { pageSize: 40, page: 1 });
|
const pageOptions = Object.assign(new PaginationComponentOptions(), { pageSize: 40, page: 1 });
|
||||||
const filters = { 'f.test': ['value'], 'f.example': ['another value', 'second value'] };
|
const filters = [new SearchFilter('f.test', ['value']), new SearchFilter('f.example', ['another value', 'second value'])];
|
||||||
const query = 'search query';
|
const query = 'search query';
|
||||||
const scope = '0fde1ecb-82cc-425a-b600-ac3576d76b47';
|
const scope = '0fde1ecb-82cc-425a-b600-ac3576d76b47';
|
||||||
const baseUrl = 'www.rest.com';
|
const baseUrl = 'www.rest.com';
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
options = new PaginatedSearchOptions();
|
options = new PaginatedSearchOptions({sort: sortOptions, pagination: pageOptions, filters: filters, query: query, scope: scope, dsoType: DSpaceObjectType.ITEM});
|
||||||
options.sort = sortOptions;
|
|
||||||
options.pagination = pageOptions;
|
|
||||||
options.filters = filters;
|
|
||||||
options.query = query;
|
|
||||||
options.scope = scope;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when toRestUrl is called', () => {
|
describe('when toRestUrl is called', () => {
|
||||||
@@ -30,6 +27,7 @@ describe('PaginatedSearchOptions', () => {
|
|||||||
'size=40&' +
|
'size=40&' +
|
||||||
'query=search query&' +
|
'query=search query&' +
|
||||||
'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' +
|
'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' +
|
||||||
|
'dsoType=ITEM&' +
|
||||||
'f.test=value,query&' +
|
'f.test=value,query&' +
|
||||||
'f.example=another value,query&' +
|
'f.example=another value,query&' +
|
||||||
'f.example=second value,query'
|
'f.example=second value,query'
|
||||||
|
@@ -2,6 +2,8 @@ import { SortOptions } from '../core/cache/models/sort-options.model';
|
|||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
import { isNotEmpty } from '../shared/empty.util';
|
import { isNotEmpty } from '../shared/empty.util';
|
||||||
import { SearchOptions } from './search-options.model';
|
import { SearchOptions } from './search-options.model';
|
||||||
|
import { SearchFilter } from './search-filter.model';
|
||||||
|
import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This model class represents all parameters needed to request information about a certain page of a search request, in a certain order
|
* This model class represents all parameters needed to request information about a certain page of a search request, in a certain order
|
||||||
@@ -10,6 +12,12 @@ export class PaginatedSearchOptions extends SearchOptions {
|
|||||||
pagination?: PaginationComponentOptions;
|
pagination?: PaginationComponentOptions;
|
||||||
sort?: SortOptions;
|
sort?: SortOptions;
|
||||||
|
|
||||||
|
constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], pagination?: PaginationComponentOptions, sort?: SortOptions}) {
|
||||||
|
super(options);
|
||||||
|
this.pagination = options.pagination;
|
||||||
|
this.sort = options.sort;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to generate the URL that can be used to request a certain page with specific sort options
|
* Method to generate the URL that can be used to request a certain page with specific sort options
|
||||||
* @param {string} url The URL to the REST endpoint
|
* @param {string} url The URL to the REST endpoint
|
||||||
|
20
src/app/+search-page/search-filter.model.ts
Normal file
20
src/app/+search-page/search-filter.model.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Represents a search filter
|
||||||
|
*/
|
||||||
|
import { hasValue } from '../shared/empty.util';
|
||||||
|
|
||||||
|
export class SearchFilter {
|
||||||
|
key: string;
|
||||||
|
values: string[];
|
||||||
|
operator: string;
|
||||||
|
|
||||||
|
constructor(key: string, values: string[], operator?: string) {
|
||||||
|
this.key = key;
|
||||||
|
this.values = values;
|
||||||
|
if (hasValue(operator)) {
|
||||||
|
this.operator = operator;
|
||||||
|
} else {
|
||||||
|
this.operator = 'query';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,6 +16,8 @@ import { SearchFilterConfig } from '../../../search-service/search-filter-config
|
|||||||
import { SearchService } from '../../../search-service/search.service';
|
import { SearchService } from '../../../search-service/search.service';
|
||||||
import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
|
import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
|
||||||
import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
|
||||||
|
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-facet-filter',
|
selector: 'ds-search-facet-filter',
|
||||||
@@ -88,13 +90,16 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
|
|||||||
return { options, page }
|
return { options, page }
|
||||||
}).switchMap(({ options, page }) => {
|
}).switchMap(({ options, page }) => {
|
||||||
return this.searchService.getFacetValuesFor(this.filterConfig, page, options)
|
return this.searchService.getFacetValuesFor(this.filterConfig, page, options)
|
||||||
.first((RD) => !RD.isLoading).map((results) => {
|
.pipe(
|
||||||
return {
|
getSucceededRemoteData(),
|
||||||
values: Observable.of(results),
|
map((results) => {
|
||||||
page: page
|
return {
|
||||||
};
|
values: Observable.of(results),
|
||||||
}
|
page: page
|
||||||
);
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
});
|
});
|
||||||
let filterValues = [];
|
let filterValues = [];
|
||||||
this.subs.push(facetValues.subscribe((facetOutcome) => {
|
this.subs.push(facetValues.subscribe((facetOutcome) => {
|
||||||
@@ -250,14 +255,15 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
|
|||||||
this.searchConfigService.searchOptions.first().subscribe(
|
this.searchConfigService.searchOptions.first().subscribe(
|
||||||
(options) => {
|
(options) => {
|
||||||
this.filterSearchResults = this.searchService.getFacetValuesFor(this.filterConfig, 1, options, data.toLowerCase())
|
this.filterSearchResults = this.searchService.getFacetValuesFor(this.filterConfig, 1, options, data.toLowerCase())
|
||||||
.first()
|
.pipe(
|
||||||
.map(
|
getSucceededRemoteData(),
|
||||||
(rd: RemoteData<PaginatedList<FacetValue>>) => {
|
map(
|
||||||
return rd.payload.page.map((facet) => {
|
(rd: RemoteData<PaginatedList<FacetValue>>) => {
|
||||||
return { displayValue: this.getDisplayValue(facet, data), value: facet.value }
|
return rd.payload.page.map((facet) => {
|
||||||
})
|
return { displayValue: this.getDisplayValue(facet, data), value: facet.value }
|
||||||
}
|
})
|
||||||
);
|
}
|
||||||
|
))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@@ -6,6 +6,7 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { SearchFilterService } from './search-filter/search-filter.service';
|
import { SearchFilterService } from './search-filter/search-filter.service';
|
||||||
|
import { getSucceededRemoteData } from '../../core/shared/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-filters',
|
selector: 'ds-search-filters',
|
||||||
@@ -35,7 +36,7 @@ export class SearchFiltersComponent {
|
|||||||
* @param {SearchFilterService} filterService
|
* @param {SearchFilterService} filterService
|
||||||
*/
|
*/
|
||||||
constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) {
|
constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) {
|
||||||
this.filters = searchService.getConfig().first((RD) => !RD.isLoading);
|
this.filters = searchService.getConfig().pipe(getSucceededRemoteData());
|
||||||
this.clearParams = searchConfigService.getCurrentFrontendFilters().map((filters) => {
|
this.clearParams = searchConfigService.getCurrentFrontendFilters().map((filters) => {
|
||||||
Object.keys(filters).forEach((f) => filters[f] = null);
|
Object.keys(filters).forEach((f) => filters[f] = null);
|
||||||
return filters;
|
return filters;
|
||||||
|
@@ -1,18 +1,17 @@
|
|||||||
import 'rxjs/add/observable/of';
|
import 'rxjs/add/observable/of';
|
||||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||||
import { SearchOptions } from './search-options.model';
|
import { SearchOptions } from './search-options.model';
|
||||||
|
import { SearchFilter } from './search-filter.model';
|
||||||
|
import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
|
||||||
|
|
||||||
describe('SearchOptions', () => {
|
describe('SearchOptions', () => {
|
||||||
let options: PaginatedSearchOptions;
|
let options: PaginatedSearchOptions;
|
||||||
const filters = { 'f.test': ['value'], 'f.example': ['another value', 'second value'] };
|
const filters = [new SearchFilter('f.test', ['value']), new SearchFilter('f.example', ['another value', 'second value'])];
|
||||||
const query = 'search query';
|
const query = 'search query';
|
||||||
const scope = '0fde1ecb-82cc-425a-b600-ac3576d76b47';
|
const scope = '0fde1ecb-82cc-425a-b600-ac3576d76b47';
|
||||||
const baseUrl = 'www.rest.com';
|
const baseUrl = 'www.rest.com';
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
options = new SearchOptions();
|
options = new SearchOptions({ filters: filters, query: query, scope: scope , dsoType: DSpaceObjectType.ITEM});
|
||||||
options.filters = filters;
|
|
||||||
options.query = query;
|
|
||||||
options.scope = scope;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when toRestUrl is called', () => {
|
describe('when toRestUrl is called', () => {
|
||||||
@@ -22,6 +21,7 @@ describe('SearchOptions', () => {
|
|||||||
expect(outcome).toEqual('www.rest.com?' +
|
expect(outcome).toEqual('www.rest.com?' +
|
||||||
'query=search query&' +
|
'query=search query&' +
|
||||||
'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' +
|
'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' +
|
||||||
|
'dsoType=ITEM&' +
|
||||||
'f.test=value,query&' +
|
'f.test=value,query&' +
|
||||||
'f.example=another value,query&' +
|
'f.example=another value,query&' +
|
||||||
'f.example=second value,query'
|
'f.example=second value,query'
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import { isNotEmpty } from '../shared/empty.util';
|
import { isNotEmpty } from '../shared/empty.util';
|
||||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
import 'core-js/library/fn/object/entries';
|
import 'core-js/library/fn/object/entries';
|
||||||
|
import { SearchFilter } from './search-filter.model';
|
||||||
|
import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This model class represents all parameters needed to request information about a certain search request
|
* This model class represents all parameters needed to request information about a certain search request
|
||||||
@@ -8,7 +10,15 @@ import 'core-js/library/fn/object/entries';
|
|||||||
export class SearchOptions {
|
export class SearchOptions {
|
||||||
scope?: string;
|
scope?: string;
|
||||||
query?: string;
|
query?: string;
|
||||||
filters?: any;
|
dsoType?: DSpaceObjectType;
|
||||||
|
filters?: SearchFilter[];
|
||||||
|
|
||||||
|
constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[]}) {
|
||||||
|
this.scope = options.scope;
|
||||||
|
this.query = options.query;
|
||||||
|
this.dsoType = options.dsoType;
|
||||||
|
this.filters = options.filters;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to generate the URL that can be used request information about a search request
|
* Method to generate the URL that can be used request information about a search request
|
||||||
@@ -21,13 +31,15 @@ export class SearchOptions {
|
|||||||
if (isNotEmpty(this.query)) {
|
if (isNotEmpty(this.query)) {
|
||||||
args.push(`query=${this.query}`);
|
args.push(`query=${this.query}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNotEmpty(this.scope)) {
|
if (isNotEmpty(this.scope)) {
|
||||||
args.push(`scope=${this.scope}`);
|
args.push(`scope=${this.scope}`);
|
||||||
}
|
}
|
||||||
|
if (isNotEmpty(this.dsoType)) {
|
||||||
|
args.push(`dsoType=${this.dsoType}`);
|
||||||
|
}
|
||||||
if (isNotEmpty(this.filters)) {
|
if (isNotEmpty(this.filters)) {
|
||||||
Object.entries(this.filters).forEach(([key, values]) => {
|
this.filters.forEach((filter: SearchFilter) => {
|
||||||
values.forEach((value) => args.push(`${key}=${value},query`));
|
filter.values.forEach((value) => args.push(`${filter.key}=${value},${filter.operator}`));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isNotEmpty(args)) {
|
if (isNotEmpty(args)) {
|
||||||
|
@@ -37,7 +37,7 @@ describe('SearchPageComponent', () => {
|
|||||||
pagination.currentPage = 1;
|
pagination.currentPage = 1;
|
||||||
pagination.pageSize = 10;
|
pagination.pageSize = 10;
|
||||||
const sort: SortOptions = new SortOptions('score', SortDirection.DESC);
|
const sort: SortOptions = new SortOptions('score', SortDirection.DESC);
|
||||||
const mockResults = Observable.of(new RemoteData(false, false, true, null,['test', 'data']));
|
const mockResults = Observable.of(new RemoteData(false, false, true, null, ['test', 'data']));
|
||||||
const searchServiceStub = jasmine.createSpyObj('SearchService', {
|
const searchServiceStub = jasmine.createSpyObj('SearchService', {
|
||||||
search: mockResults,
|
search: mockResults,
|
||||||
getSearchLink: '/search',
|
getSearchLink: '/search',
|
||||||
@@ -46,11 +46,11 @@ describe('SearchPageComponent', () => {
|
|||||||
const queryParam = 'test query';
|
const queryParam = 'test query';
|
||||||
const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
||||||
const paginatedSearchOptions = {
|
const paginatedSearchOptions = {
|
||||||
query: queryParam,
|
query: queryParam,
|
||||||
scope: scopeParam,
|
scope: scopeParam,
|
||||||
pagination,
|
pagination,
|
||||||
sort
|
sort
|
||||||
};
|
};
|
||||||
const activatedRouteStub = {
|
const activatedRouteStub = {
|
||||||
queryParams: Observable.of({
|
queryParams: Observable.of({
|
||||||
query: queryParam,
|
query: queryParam,
|
||||||
@@ -179,4 +179,3 @@ describe('SearchPageComponent', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
;
|
|
||||||
|
@@ -15,6 +15,7 @@ import { Subscription } from 'rxjs/Subscription';
|
|||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||||
|
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
@@ -78,7 +79,7 @@ export class SearchPageComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
||||||
this.sub = this.searchOptions$
|
this.sub = this.searchOptions$
|
||||||
.switchMap((options) => this.service.search(options).filter((rd) => !rd.isLoading).first())
|
.switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData()))
|
||||||
.subscribe((results) => {
|
.subscribe((results) => {
|
||||||
this.resultsRD$.next(results);
|
this.resultsRD$.next(results);
|
||||||
});
|
});
|
||||||
|
@@ -4,26 +4,27 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
|
|||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { SearchFilter } from '../search-filter.model';
|
||||||
|
|
||||||
describe('SearchConfigurationService', () => {
|
describe('SearchConfigurationService', () => {
|
||||||
let service: SearchConfigurationService;
|
let service: SearchConfigurationService;
|
||||||
const value1 = 'random value';
|
const value1 = 'random value';
|
||||||
const value2 = 'another value';
|
|
||||||
const prefixFilter = {
|
const prefixFilter = {
|
||||||
'f.author': ['another value'],
|
'f.author': ['another value'],
|
||||||
'f.date.min': ['2013'],
|
'f.date.min': ['2013'],
|
||||||
'f.date.max': ['2018']
|
'f.date.max': ['2018']
|
||||||
};
|
};
|
||||||
const defaults = Object.assign(new PaginatedSearchOptions(), {
|
const defaults = new PaginatedSearchOptions({
|
||||||
pagination: Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 20 }),
|
pagination: Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 20 }),
|
||||||
sort: new SortOptions('score', SortDirection.DESC),
|
sort: new SortOptions('score', SortDirection.DESC),
|
||||||
query: '',
|
query: '',
|
||||||
scope: ''
|
scope: ''
|
||||||
});
|
});
|
||||||
const backendFilters = { 'f.author': ['another value'], 'f.date': ['[2013 TO 2018]'] };
|
|
||||||
|
const backendFilters = [new SearchFilter('f.author', ['another value']), new SearchFilter('f.date', ['[2013 TO 2018]'])];
|
||||||
|
|
||||||
const spy = jasmine.createSpyObj('RouteService', {
|
const spy = jasmine.createSpyObj('RouteService', {
|
||||||
getQueryParameterValue: Observable.of([value1, value2]),
|
getQueryParameterValue: Observable.of(value1),
|
||||||
getQueryParamsWithPrefix: Observable.of(prefixFilter)
|
getQueryParamsWithPrefix: Observable.of(prefixFilter)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -51,6 +52,15 @@ describe('SearchConfigurationService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when getCurrentDSOType is called', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
service.getCurrentDSOType();
|
||||||
|
});
|
||||||
|
it('should call getQueryParameterValue on the routeService with parameter name \'dsoType\'', () => {
|
||||||
|
expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('dsoType');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('when getCurrentFrontendFilters is called', () => {
|
describe('when getCurrentFrontendFilters is called', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service.getCurrentFrontendFilters();
|
service.getCurrentFrontendFilters();
|
||||||
@@ -101,6 +111,7 @@ describe('SearchConfigurationService', () => {
|
|||||||
spyOn(service, 'getCurrentSort').and.callThrough();
|
spyOn(service, 'getCurrentSort').and.callThrough();
|
||||||
spyOn(service, 'getCurrentScope').and.callThrough();
|
spyOn(service, 'getCurrentScope').and.callThrough();
|
||||||
spyOn(service, 'getCurrentQuery').and.callThrough();
|
spyOn(service, 'getCurrentQuery').and.callThrough();
|
||||||
|
spyOn(service, 'getCurrentDSOType').and.callThrough();
|
||||||
spyOn(service, 'getCurrentFilters').and.callThrough();
|
spyOn(service, 'getCurrentFilters').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -113,6 +124,7 @@ describe('SearchConfigurationService', () => {
|
|||||||
expect(service.getCurrentSort).not.toHaveBeenCalled();
|
expect(service.getCurrentSort).not.toHaveBeenCalled();
|
||||||
expect(service.getCurrentScope).toHaveBeenCalled();
|
expect(service.getCurrentScope).toHaveBeenCalled();
|
||||||
expect(service.getCurrentQuery).toHaveBeenCalled();
|
expect(service.getCurrentQuery).toHaveBeenCalled();
|
||||||
|
expect(service.getCurrentDSOType).toHaveBeenCalled();
|
||||||
expect(service.getCurrentFilters).toHaveBeenCalled();
|
expect(service.getCurrentFilters).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -126,6 +138,7 @@ describe('SearchConfigurationService', () => {
|
|||||||
expect(service.getCurrentSort).toHaveBeenCalled();
|
expect(service.getCurrentSort).toHaveBeenCalled();
|
||||||
expect(service.getCurrentScope).toHaveBeenCalled();
|
expect(service.getCurrentScope).toHaveBeenCalled();
|
||||||
expect(service.getCurrentQuery).toHaveBeenCalled();
|
expect(service.getCurrentQuery).toHaveBeenCalled();
|
||||||
|
expect(service.getCurrentDSOType).toHaveBeenCalled();
|
||||||
expect(service.getCurrentFilters).toHaveBeenCalled();
|
expect(service.getCurrentFilters).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -6,10 +6,13 @@ import { ActivatedRoute, Params } from '@angular/router';
|
|||||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||||
import { Injectable, OnDestroy } from '@angular/core';
|
import { Injectable, OnDestroy } from '@angular/core';
|
||||||
import { RouteService } from '../../shared/services/route.service';
|
import { RouteService } from '../../shared/services/route.service';
|
||||||
import { hasNoValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
import { getSucceededRemoteData } from '../../core/shared/operators';
|
||||||
|
import { SearchFilter } from '../search-filter.model';
|
||||||
|
import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all actions that have to do with the current search configuration
|
* Service that performs all actions that have to do with the current search configuration
|
||||||
@@ -67,15 +70,17 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
constructor(private routeService: RouteService,
|
constructor(private routeService: RouteService,
|
||||||
private route: ActivatedRoute) {
|
private route: ActivatedRoute) {
|
||||||
this.defaults.first().subscribe((defRD) => {
|
this.defaults
|
||||||
const defs = defRD.payload;
|
.pipe(getSucceededRemoteData())
|
||||||
this.paginatedSearchOptions = new BehaviorSubject<SearchOptions>(defs);
|
.subscribe((defRD) => {
|
||||||
this.searchOptions = new BehaviorSubject<PaginatedSearchOptions>(defs);
|
const defs = defRD.payload;
|
||||||
|
this.paginatedSearchOptions = new BehaviorSubject<SearchOptions>(defs);
|
||||||
|
this.searchOptions = new BehaviorSubject<PaginatedSearchOptions>(defs);
|
||||||
|
|
||||||
this.subs.push(this.subscribeToSearchOptions(defs));
|
this.subs.push(this.subscribeToSearchOptions(defs));
|
||||||
this.subs.push(this.subscribeToPaginatedSearchOptions(defs));
|
this.subs.push(this.subscribeToPaginatedSearchOptions(defs));
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,6 +101,15 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Observable<number>} Emits the current DSpaceObject type as a number
|
||||||
|
*/
|
||||||
|
getCurrentDSOType(): Observable<DSpaceObjectType> {
|
||||||
|
return this.routeService.getQueryParameterValue('dsoType')
|
||||||
|
.filter((type) => hasValue(type) && hasValue(DSpaceObjectType[type.toUpperCase()]))
|
||||||
|
.map((type) => DSpaceObjectType[type.toUpperCase()]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Observable<string>} Emits the current pagination settings
|
* @returns {Observable<string>} Emits the current pagination settings
|
||||||
*/
|
*/
|
||||||
@@ -130,25 +144,25 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* @returns {Observable<Params>} Emits the current active filters with their values as they are sent to the backend
|
* @returns {Observable<Params>} Emits the current active filters with their values as they are sent to the backend
|
||||||
*/
|
*/
|
||||||
getCurrentFilters(): Observable<Params> {
|
getCurrentFilters(): Observable<SearchFilter[]> {
|
||||||
return this.routeService.getQueryParamsWithPrefix('f.').map((filterParams) => {
|
return this.routeService.getQueryParamsWithPrefix('f.').map((filterParams) => {
|
||||||
if (isNotEmpty(filterParams)) {
|
if (isNotEmpty(filterParams)) {
|
||||||
const params = {};
|
const filters = [];
|
||||||
Object.keys(filterParams).forEach((key) => {
|
Object.keys(filterParams).forEach((key) => {
|
||||||
if (key.endsWith('.min') || key.endsWith('.max')) {
|
if (key.endsWith('.min') || key.endsWith('.max')) {
|
||||||
const realKey = key.slice(0, -4);
|
const realKey = key.slice(0, -4);
|
||||||
if (isEmpty(params[realKey])) {
|
if (hasNoValue(filters.find((filter) => filter.key === realKey))) {
|
||||||
const min = filterParams[realKey + '.min'] ? filterParams[realKey + '.min'][0] : '*';
|
const min = filterParams[realKey + '.min'] ? filterParams[realKey + '.min'][0] : '*';
|
||||||
const max = filterParams[realKey + '.max'] ? filterParams[realKey + '.max'][0] : '*';
|
const max = filterParams[realKey + '.max'] ? filterParams[realKey + '.max'][0] : '*';
|
||||||
params[realKey] = ['[' + min + ' TO ' + max + ']'];
|
filters.push(new SearchFilter(realKey, ['[' + min + ' TO ' + max + ']']));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
params[key] = filterParams[key];
|
filters.push(new SearchFilter(key, filterParams[key]));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return params;
|
return filters;
|
||||||
}
|
}
|
||||||
return filterParams;
|
return [];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,10 +182,11 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
return Observable.merge(
|
return Observable.merge(
|
||||||
this.getScopePart(defaults.scope),
|
this.getScopePart(defaults.scope),
|
||||||
this.getQueryPart(defaults.query),
|
this.getQueryPart(defaults.query),
|
||||||
|
this.getDSOTypePart(),
|
||||||
this.getFiltersPart()
|
this.getFiltersPart()
|
||||||
).subscribe((update) => {
|
).subscribe((update) => {
|
||||||
const currentValue: SearchOptions = this.searchOptions.getValue();
|
const currentValue: SearchOptions = this.searchOptions.getValue();
|
||||||
const updatedValue: SearchOptions = Object.assign(new SearchOptions(), currentValue, update);
|
const updatedValue: SearchOptions = Object.assign(currentValue, update);
|
||||||
this.searchOptions.next(updatedValue);
|
this.searchOptions.next(updatedValue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -187,10 +202,11 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
this.getSortPart(defaults.sort),
|
this.getSortPart(defaults.sort),
|
||||||
this.getScopePart(defaults.scope),
|
this.getScopePart(defaults.scope),
|
||||||
this.getQueryPart(defaults.query),
|
this.getQueryPart(defaults.query),
|
||||||
|
this.getDSOTypePart(),
|
||||||
this.getFiltersPart()
|
this.getFiltersPart()
|
||||||
).subscribe((update) => {
|
).subscribe((update) => {
|
||||||
const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue();
|
const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue();
|
||||||
const updatedValue: PaginatedSearchOptions = Object.assign(new PaginatedSearchOptions(), currentValue, update);
|
const updatedValue: PaginatedSearchOptions = Object.assign(currentValue, update);
|
||||||
this.paginatedSearchOptions.next(updatedValue);
|
this.paginatedSearchOptions.next(updatedValue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -200,7 +216,7 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
get defaults(): Observable<RemoteData<PaginatedSearchOptions>> {
|
get defaults(): Observable<RemoteData<PaginatedSearchOptions>> {
|
||||||
if (hasNoValue(this._defaults)) {
|
if (hasNoValue(this._defaults)) {
|
||||||
const options = Object.assign(new PaginatedSearchOptions(), {
|
const options = new PaginatedSearchOptions({
|
||||||
pagination: this.defaultPagination,
|
pagination: this.defaultPagination,
|
||||||
sort: this.defaultSort,
|
sort: this.defaultSort,
|
||||||
scope: this.defaultScope,
|
scope: this.defaultScope,
|
||||||
@@ -238,6 +254,15 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Observable<string>} Emits the current query string as a partial SearchOptions object
|
||||||
|
*/
|
||||||
|
private getDSOTypePart(): Observable<any> {
|
||||||
|
return this.getCurrentDSOType().map((dsoType) => {
|
||||||
|
return { dsoType }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Observable<string>} Emits the current pagination settings as a partial SearchOptions object
|
* @returns {Observable<string>} Emits the current pagination settings as a partial SearchOptions object
|
||||||
*/
|
*/
|
||||||
|
@@ -155,7 +155,7 @@ describe('SearchService', () => {
|
|||||||
|
|
||||||
describe('when search is called', () => {
|
describe('when search is called', () => {
|
||||||
const endPoint = 'http://endpoint.com/test/test';
|
const endPoint = 'http://endpoint.com/test/test';
|
||||||
const searchOptions = new PaginatedSearchOptions();
|
const searchOptions = new PaginatedSearchOptions({});
|
||||||
const queryResponse = Object.assign(new SearchQueryResponse(), { objects: [] });
|
const queryResponse = Object.assign(new SearchQueryResponse(), { objects: [] });
|
||||||
const response = new SearchSuccessResponse(queryResponse, '200');
|
const response = new SearchSuccessResponse(queryResponse, '200');
|
||||||
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
||||||
|
@@ -7,6 +7,9 @@
|
|||||||
</ds-notifications-board>
|
</ds-notifications-board>
|
||||||
|
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
|
<div class="container" *ngIf="isLoading">
|
||||||
|
<ds-loading message="{{'loading.default' | translate}}"></ds-loading>
|
||||||
|
</div>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@ import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
|||||||
import { AngularticsMock } from './shared/mocks/mock-angulartics.service';
|
import { AngularticsMock } from './shared/mocks/mock-angulartics.service';
|
||||||
import { AuthServiceMock } from './shared/mocks/mock-auth.service';
|
import { AuthServiceMock } from './shared/mocks/mock-auth.service';
|
||||||
import { AuthService } from './core/auth/auth.service';
|
import { AuthService } from './core/auth/auth.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
let comp: AppComponent;
|
let comp: AppComponent;
|
||||||
let fixture: ComponentFixture<AppComponent>;
|
let fixture: ComponentFixture<AppComponent>;
|
||||||
@@ -62,6 +63,7 @@ describe('App component', () => {
|
|||||||
{ provide: MetadataService, useValue: new MockMetadataService() },
|
{ provide: MetadataService, useValue: new MockMetadataService() },
|
||||||
{ provide: Angulartics2GoogleAnalytics, useValue: new AngularticsMock() },
|
{ provide: Angulartics2GoogleAnalytics, useValue: new AngularticsMock() },
|
||||||
{ provide: AuthService, useValue: new AuthServiceMock() },
|
{ provide: AuthService, useValue: new AuthServiceMock() },
|
||||||
|
{ provide: Router, useValue: {} },
|
||||||
AppComponent
|
AppComponent
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
import { ChangeDetectionStrategy, Component, HostListener, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
HostListener,
|
||||||
|
Inject,
|
||||||
|
OnInit,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { NavigationCancel, NavigationEnd, NavigationStart, Router } from '@angular/router';
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
@@ -21,7 +30,8 @@ import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit, AfterViewInit {
|
||||||
|
isLoading = true;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
|
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
|
||||||
@@ -30,7 +40,8 @@ export class AppComponent implements OnInit {
|
|||||||
private store: Store<HostWindowState>,
|
private store: Store<HostWindowState>,
|
||||||
private metadata: MetadataService,
|
private metadata: MetadataService,
|
||||||
private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics,
|
private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics,
|
||||||
private authService: AuthService
|
private authService: AuthService,
|
||||||
|
private router: Router
|
||||||
) {
|
) {
|
||||||
// this language will be used as a fallback when a translation isn't found in the current language
|
// this language will be used as a fallback when a translation isn't found in the current language
|
||||||
translate.setDefaultLang('en');
|
translate.setDefaultLang('en');
|
||||||
@@ -58,6 +69,20 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.router.events
|
||||||
|
.subscribe((event) => {
|
||||||
|
if (event instanceof NavigationStart) {
|
||||||
|
this.isLoading = true;
|
||||||
|
} else if (
|
||||||
|
event instanceof NavigationEnd ||
|
||||||
|
event instanceof NavigationCancel
|
||||||
|
) {
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
private onResize(event): void {
|
private onResize(event): void {
|
||||||
this.dispatchWindowSize(event.target.innerWidth, event.target.innerHeight);
|
this.dispatchWindowSize(event.target.innerWidth, event.target.innerHeight);
|
||||||
|
7
src/app/core/shared/dspace-object-type.model.ts
Normal file
7
src/app/core/shared/dspace-object-type.model.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export enum DSpaceObjectType {
|
||||||
|
BUNDLE = 'BUNDLE',
|
||||||
|
BITSTREAM = 'BITSTREAM',
|
||||||
|
ITEM = 'ITEM',
|
||||||
|
COLLECTION = 'COLLECTION',
|
||||||
|
COMMUNITY = 'COMMUNITY',
|
||||||
|
}
|
@@ -9,6 +9,9 @@ import { RestRequest } from '../data/request.models';
|
|||||||
import { RequestEntry } from '../data/request.reducer';
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { BrowseDefinition } from './browse-definition.model';
|
import { BrowseDefinition } from './browse-definition.model';
|
||||||
|
import { DSpaceObject } from './dspace-object.model';
|
||||||
|
import { PaginatedList } from '../data/paginated-list';
|
||||||
|
import { SearchResult } from '../../+search-page/search-result.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file contains custom RxJS operators that can be used in multiple places
|
* This file contains custom RxJS operators that can be used in multiple places
|
||||||
@@ -51,6 +54,16 @@ export const getSucceededRemoteData = () =>
|
|||||||
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
|
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
|
||||||
source.pipe(first((rd: RemoteData<T>) => rd.hasSucceeded));
|
source.pipe(first((rd: RemoteData<T>) => rd.hasSucceeded));
|
||||||
|
|
||||||
|
export const toDSpaceObjectListRD = () =>
|
||||||
|
<T extends DSpaceObject>(source: Observable<RemoteData<PaginatedList<SearchResult<T>>>>): Observable<RemoteData<PaginatedList<T>>> =>
|
||||||
|
source.pipe(
|
||||||
|
map((rd: RemoteData<PaginatedList<SearchResult<T>>>) => {
|
||||||
|
const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult<T>) => searchResult.dspaceObject);
|
||||||
|
const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList<T>;
|
||||||
|
return Object.assign(rd, {payload: payload});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the browse links from a definition by ID given an array of all definitions
|
* Get the browse links from a definition by ID given an array of all definitions
|
||||||
* @param {string} definitionID
|
* @param {string} definitionID
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<img *ngIf="thumbnail" [src]="thumbnail.content" (error)="errorHandler($event)"/>
|
<img *ngIf="thumbnail" [src]="thumbnail.content" (error)="errorHandler($event)" class="img-fluid"/>
|
||||||
<img *ngIf="!thumbnail" [src]="holderSource | dsSafeUrl"/>
|
<img *ngIf="!thumbnail" [src]="holderSource | dsSafeUrl" class="img-fluid"/>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user