mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-15 22:13:02 +00:00
cleaned up remotedata for a better separation of concerns, moved statuscode and errormsg in to RemoteDataError object, moved pageInfo to PaginatedList object in the payload
This commit is contained in:
@@ -6,6 +6,7 @@ import { Subscription } from 'rxjs/Subscription';
|
|||||||
import { SortOptions } from '../core/cache/models/sort-options.model';
|
import { 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 { ItemDataService } from '../core/data/item-data.service';
|
||||||
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
|
|
||||||
import { MetadataService } from '../core/metadata/metadata.service';
|
import { MetadataService } from '../core/metadata/metadata.service';
|
||||||
@@ -30,7 +31,7 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp
|
|||||||
})
|
})
|
||||||
export class CollectionPageComponent implements OnInit, OnDestroy {
|
export class CollectionPageComponent implements OnInit, OnDestroy {
|
||||||
collectionRDObs: Observable<RemoteData<Collection>>;
|
collectionRDObs: Observable<RemoteData<Collection>>;
|
||||||
itemRDObs: Observable<RemoteData<Item[]>>;
|
itemRDObs: Observable<RemoteData<PaginatedList<Item>>>;
|
||||||
logoRDObs: Observable<RemoteData<Bitstream>>;
|
logoRDObs: Observable<RemoteData<Bitstream>>;
|
||||||
paginationConfig: PaginationComponentOptions;
|
paginationConfig: PaginationComponentOptions;
|
||||||
sortConfig: SortOptions;
|
sortConfig: SortOptions;
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div *ngIf="subCollectionsRD?.hasSucceeded" @fadeIn>
|
<div *ngIf="subCollectionsRD?.hasSucceeded" @fadeIn>
|
||||||
<h2>{{'community.sub-collection-list.head' | translate}}</h2>
|
<h2>{{'community.sub-collection-list.head' | translate}}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let collection of subCollectionsRD?.payload">
|
<li *ngFor="let collection of subCollectionsRD?.payload?.page">
|
||||||
<p>
|
<p>
|
||||||
<span class="lead"><a [routerLink]="['/collections', collection.id]">{{collection.name}}</a></span><br>
|
<span class="lead"><a [routerLink]="['/collections', collection.id]">{{collection.name}}</a></span><br>
|
||||||
<span class="text-muted">{{collection.shortDescription}}</span>
|
<span class="text-muted">{{collection.shortDescription}}</span>
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||||
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
|
|
||||||
import { fadeIn } from '../../shared/animations/fade';
|
import { fadeIn } from '../../shared/animations/fade';
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-community-page-sub-collection-list',
|
selector: 'ds-community-page-sub-collection-list',
|
||||||
@@ -14,7 +15,7 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
animations:[fadeIn]
|
animations:[fadeIn]
|
||||||
})
|
})
|
||||||
export class CommunityPageSubCollectionListComponent implements OnInit {
|
export class CommunityPageSubCollectionListComponent implements OnInit {
|
||||||
subCollectionsRDObs: Observable<RemoteData<Collection[]>>;
|
subCollectionsRDObs: Observable<RemoteData<PaginatedList<Collection>>>;
|
||||||
|
|
||||||
constructor(private cds: CollectionDataService) {
|
constructor(private cds: CollectionDataService) {
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||||
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
|
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Community } from '../../core/shared/community.model';
|
import { Community } from '../../core/shared/community.model';
|
||||||
@@ -17,7 +18,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
|
|||||||
animations: [fadeInOut]
|
animations: [fadeInOut]
|
||||||
})
|
})
|
||||||
export class TopLevelCommunityListComponent {
|
export class TopLevelCommunityListComponent {
|
||||||
communitiesRDObs: Observable<RemoteData<Community[]>>;
|
communitiesRDObs: Observable<RemoteData<PaginatedList<Community>>>;
|
||||||
config: PaginationComponentOptions;
|
config: PaginationComponentOptions;
|
||||||
sortConfig: SortOptions;
|
sortConfig: SortOptions;
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
[query]="query"
|
[query]="query"
|
||||||
[scope]="(scopeObjectRDObs | async)?.payload"
|
[scope]="(scopeObjectRDObs | async)?.payload"
|
||||||
[currentParams]="currentParams"
|
[currentParams]="currentParams"
|
||||||
[scopes]="(scopeListRDObs | async)?.payload">
|
[scopes]="(scopeListRDObs | async)?.payload?.page">
|
||||||
</ds-search-form>
|
</ds-search-form>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div id="search-body"
|
<div id="search-body"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
| translate}}
|
| translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ds-search-results [searchResults]="resultsRDObs | async"
|
<ds-search-results [searchResults]="(resultsRDObs | async)?.page"
|
||||||
[searchConfig]="searchOptions"></ds-search-results>
|
[searchConfig]="searchOptions"></ds-search-results>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { CommunityDataService } from '../core/data/community-data.service';
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { Community } from '../core/shared/community.model';
|
import { Community } from '../core/shared/community.model';
|
||||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||||
@@ -36,7 +37,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
|
|||||||
resultsRDObs: Observable<RemoteData<Array<SearchResult<DSpaceObject>>>>;
|
resultsRDObs: Observable<RemoteData<Array<SearchResult<DSpaceObject>>>>;
|
||||||
currentParams = {};
|
currentParams = {};
|
||||||
searchOptions: SearchOptions;
|
searchOptions: SearchOptions;
|
||||||
scopeListRDObs: Observable<RemoteData<Community[]>>;
|
scopeListRDObs: Observable<RemoteData<PaginatedList<Community>>>;
|
||||||
isMobileView: Observable<boolean>;
|
isMobileView: Observable<boolean>;
|
||||||
|
|
||||||
constructor(private service: SearchService,
|
constructor(private service: SearchService,
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import { Injectable, OnDestroy } from '@angular/core';
|
import { Injectable, OnDestroy } from '@angular/core';
|
||||||
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { RemoteDataError } from '../../core/data/remote-data-error';
|
||||||
import { SearchResult } from '../search-result.model';
|
import { SearchResult } from '../search-result.model';
|
||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
@@ -100,26 +102,7 @@ export class SearchService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
search(query: string, scopeId?: string, searchOptions?: SearchOptions): Observable<RemoteData<Array<SearchResult<DSpaceObject>>>> {
|
search(query: string, scopeId?: string, searchOptions?: SearchOptions): Observable<RemoteData<Array<SearchResult<DSpaceObject>>>> {
|
||||||
this.searchOptions = this.searchOptions;
|
const error = new RemoteDataError('200', undefined);
|
||||||
let self = `https://dspace7.4science.it/dspace-spring-rest/api/search?query=${query}`;
|
|
||||||
if (hasValue(scopeId)) {
|
|
||||||
self += `&scope=${scopeId}`;
|
|
||||||
}
|
|
||||||
if (isNotEmpty(searchOptions) && hasValue(searchOptions.pagination.currentPage)) {
|
|
||||||
self += `&page=${searchOptions.pagination.currentPage}`;
|
|
||||||
}
|
|
||||||
if (isNotEmpty(searchOptions) && hasValue(searchOptions.pagination.pageSize)) {
|
|
||||||
self += `&pageSize=${searchOptions.pagination.pageSize}`;
|
|
||||||
}
|
|
||||||
if (isNotEmpty(searchOptions) && hasValue(searchOptions.sort.direction)) {
|
|
||||||
self += `&sortDirection=${searchOptions.sort.direction}`;
|
|
||||||
}
|
|
||||||
if (isNotEmpty(searchOptions) && hasValue(searchOptions.sort.field)) {
|
|
||||||
self += `&sortField=${searchOptions.sort.field}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const errorMessage = undefined;
|
|
||||||
const statusCode = '200';
|
|
||||||
const returningPageInfo = new PageInfo();
|
const returningPageInfo = new PageInfo();
|
||||||
|
|
||||||
if (isNotEmpty(searchOptions)) {
|
if (isNotEmpty(searchOptions)) {
|
||||||
@@ -137,13 +120,12 @@ export class SearchService implements OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return itemsObs
|
return itemsObs
|
||||||
.filter((rd: RemoteData<Item[]>) => rd.hasSucceeded)
|
.filter((rd: RemoteData<PaginatedList<Item>>) => rd.hasSucceeded)
|
||||||
.map((rd: RemoteData<Item[]>) => {
|
.map((rd: RemoteData<PaginatedList<Item>>) => {
|
||||||
|
|
||||||
const totalElements = rd.pageInfo.totalElements > 20 ? 20 : rd.pageInfo.totalElements;
|
const totalElements = rd.payload.totalElements > 20 ? 20 : rd.payload.totalElements;
|
||||||
const pageInfo = Object.assign({}, rd.pageInfo, { totalElements: totalElements });
|
|
||||||
|
|
||||||
const payload = shuffle(rd.payload)
|
const page = shuffle(rd.payload.page)
|
||||||
.map((item: Item, index: number) => {
|
.map((item: Item, index: number) => {
|
||||||
const mockResult: SearchResult<DSpaceObject> = new ItemSearchResult();
|
const mockResult: SearchResult<DSpaceObject> = new ItemSearchResult();
|
||||||
mockResult.dspaceObject = item;
|
mockResult.dspaceObject = item;
|
||||||
@@ -154,24 +136,20 @@ export class SearchService implements OnDestroy {
|
|||||||
return mockResult;
|
return mockResult;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const payload = Object.assign({}, rd.payload, { totalElements: totalElements, page });
|
||||||
|
|
||||||
return new RemoteData(
|
return new RemoteData(
|
||||||
self,
|
|
||||||
rd.isRequestPending,
|
rd.isRequestPending,
|
||||||
rd.isResponsePending,
|
rd.isResponsePending,
|
||||||
rd.hasSucceeded,
|
rd.hasSucceeded,
|
||||||
errorMessage,
|
error,
|
||||||
statusCode,
|
|
||||||
pageInfo,
|
|
||||||
payload
|
payload
|
||||||
)
|
)
|
||||||
}).startWith(new RemoteData(
|
}).startWith(new RemoteData(
|
||||||
'',
|
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined
|
undefined
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -180,17 +158,12 @@ export class SearchService implements OnDestroy {
|
|||||||
const requestPending = false;
|
const requestPending = false;
|
||||||
const responsePending = false;
|
const responsePending = false;
|
||||||
const isSuccessful = true;
|
const isSuccessful = true;
|
||||||
const errorMessage = undefined;
|
const error = new RemoteDataError('200', undefined);
|
||||||
const statusCode = '200';
|
|
||||||
const returningPageInfo = new PageInfo();
|
|
||||||
return Observable.of(new RemoteData(
|
return Observable.of(new RemoteData(
|
||||||
'https://dspace7.4science.it/dspace-spring-rest/api/search',
|
|
||||||
requestPending,
|
requestPending,
|
||||||
responsePending,
|
responsePending,
|
||||||
isSuccessful,
|
isSuccessful,
|
||||||
errorMessage,
|
error,
|
||||||
statusCode,
|
|
||||||
returningPageInfo,
|
|
||||||
this.config
|
this.config
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -198,12 +171,12 @@ export class SearchService implements OnDestroy {
|
|||||||
getFacetValuesFor(searchFilterConfigName: string): Observable<RemoteData<FacetValue[]>> {
|
getFacetValuesFor(searchFilterConfigName: string): Observable<RemoteData<FacetValue[]>> {
|
||||||
const filterConfig = this.config.find((config: SearchFilterConfig) => config.name === searchFilterConfigName);
|
const filterConfig = this.config.find((config: SearchFilterConfig) => config.name === searchFilterConfigName);
|
||||||
return this.routeService.getQueryParameterValues(filterConfig.paramName).map((selectedValues: string[]) => {
|
return this.routeService.getQueryParameterValues(filterConfig.paramName).map((selectedValues: string[]) => {
|
||||||
const values: FacetValue[] = [];
|
const payload: FacetValue[] = [];
|
||||||
const totalFilters = 13;
|
const totalFilters = 13;
|
||||||
for (let i = 0; i < totalFilters; i++) {
|
for (let i = 0; i < totalFilters; i++) {
|
||||||
const value = searchFilterConfigName + ' ' + (i + 1);
|
const value = searchFilterConfigName + ' ' + (i + 1);
|
||||||
if (!selectedValues.includes(value)) {
|
if (!selectedValues.includes(value)) {
|
||||||
values.push({
|
payload.push({
|
||||||
value: value,
|
value: value,
|
||||||
count: Math.floor(Math.random() * 20) + 20 * (totalFilters - i), // make sure first results have the highest (random) count
|
count: Math.floor(Math.random() * 20) + 20 * (totalFilters - i), // make sure first results have the highest (random) count
|
||||||
search: decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value
|
search: decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value
|
||||||
@@ -213,18 +186,13 @@ export class SearchService implements OnDestroy {
|
|||||||
const requestPending = false;
|
const requestPending = false;
|
||||||
const responsePending = false;
|
const responsePending = false;
|
||||||
const isSuccessful = true;
|
const isSuccessful = true;
|
||||||
const errorMessage = undefined;
|
const error = new RemoteDataError('200', undefined);;
|
||||||
const statusCode = '200';
|
|
||||||
const returningPageInfo = new PageInfo();
|
|
||||||
return new RemoteData(
|
return new RemoteData(
|
||||||
'https://dspace7.4science.it/dspace-spring-rest/api/search',
|
|
||||||
requestPending,
|
requestPending,
|
||||||
responsePending,
|
responsePending,
|
||||||
isSuccessful,
|
isSuccessful,
|
||||||
errorMessage,
|
error,
|
||||||
statusCode,
|
payload
|
||||||
returningPageInfo,
|
|
||||||
values
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
|
import { RemoteDataError } from '../../data/remote-data-error';
|
||||||
|
|
||||||
import { CacheableObject } from '../object-cache.reducer';
|
import { CacheableObject } from '../object-cache.reducer';
|
||||||
import { ObjectCacheService } from '../object-cache.service';
|
import { ObjectCacheService } from '../object-cache.service';
|
||||||
@@ -88,32 +90,18 @@ export class RemoteDataBuildService {
|
|||||||
const requestPending = hasValue(reqEntry.requestPending) ? reqEntry.requestPending : true;
|
const requestPending = hasValue(reqEntry.requestPending) ? reqEntry.requestPending : true;
|
||||||
const responsePending = hasValue(reqEntry.responsePending) ? reqEntry.responsePending : false;
|
const responsePending = hasValue(reqEntry.responsePending) ? reqEntry.responsePending : false;
|
||||||
let isSuccessFul: boolean;
|
let isSuccessFul: boolean;
|
||||||
let errorMessage: string;
|
let error: RemoteDataError;
|
||||||
let statusCode: string;
|
|
||||||
let pageInfo: PageInfo;
|
|
||||||
if (hasValue(resEntry) && hasValue(resEntry.response)) {
|
if (hasValue(resEntry) && hasValue(resEntry.response)) {
|
||||||
isSuccessFul = resEntry.response.isSuccessful;
|
isSuccessFul = resEntry.response.isSuccessful;
|
||||||
errorMessage = isSuccessFul === false ? (resEntry.response as ErrorResponse).errorMessage : undefined;
|
const errorMessage = isSuccessFul === false ? (resEntry.response as ErrorResponse).errorMessage : undefined;
|
||||||
statusCode = resEntry.response.statusCode;
|
error = new RemoteDataError(resEntry.response.statusCode, errorMessage);
|
||||||
|
|
||||||
if (hasValue((resEntry.response as DSOSuccessResponse).pageInfo)) {
|
|
||||||
const resPageInfo = (resEntry.response as DSOSuccessResponse).pageInfo;
|
|
||||||
if (isNotEmpty(resPageInfo) && resPageInfo.currentPage >= 0) {
|
|
||||||
pageInfo = Object.assign({}, resPageInfo, { currentPage: resPageInfo.currentPage + 1 });
|
|
||||||
} else {
|
|
||||||
pageInfo = resPageInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RemoteData(
|
return new RemoteData(
|
||||||
href,
|
|
||||||
requestPending,
|
requestPending,
|
||||||
responsePending,
|
responsePending,
|
||||||
isSuccessFul,
|
isSuccessFul,
|
||||||
errorMessage,
|
error,
|
||||||
statusCode,
|
|
||||||
pageInfo,
|
|
||||||
payload
|
payload
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -122,7 +110,7 @@ export class RemoteDataBuildService {
|
|||||||
buildList<TNormalized extends CacheableObject, TDomain>(
|
buildList<TNormalized extends CacheableObject, TDomain>(
|
||||||
hrefObs: string | Observable<string>,
|
hrefObs: string | Observable<string>,
|
||||||
normalizedType: GenericConstructor<TNormalized>
|
normalizedType: GenericConstructor<TNormalized>
|
||||||
): Observable<RemoteData<TDomain[]>> {
|
): Observable<RemoteData<TDomain[] | PaginatedList<TDomain>>> {
|
||||||
if (typeof hrefObs === 'string') {
|
if (typeof hrefObs === 'string') {
|
||||||
hrefObs = Observable.of(hrefObs);
|
hrefObs = Observable.of(hrefObs);
|
||||||
}
|
}
|
||||||
@@ -132,7 +120,7 @@ export class RemoteDataBuildService {
|
|||||||
const responseCacheObs = hrefObs.flatMap((href: string) => this.responseCache.get(href))
|
const responseCacheObs = hrefObs.flatMap((href: string) => this.responseCache.get(href))
|
||||||
.filter((entry) => hasValue(entry));
|
.filter((entry) => hasValue(entry));
|
||||||
|
|
||||||
const payloadObs = responseCacheObs
|
const tDomainListObs = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||||
.map((entry: ResponseCacheEntry) => (entry.response as DSOSuccessResponse).resourceSelfLinks)
|
.map((entry: ResponseCacheEntry) => (entry.response as DSOSuccessResponse).resourceSelfLinks)
|
||||||
.flatMap((resourceUUIDs: string[]) => {
|
.flatMap((resourceUUIDs: string[]) => {
|
||||||
@@ -146,6 +134,27 @@ export class RemoteDataBuildService {
|
|||||||
.startWith([])
|
.startWith([])
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
|
|
||||||
|
const pageInfoObs = responseCacheObs
|
||||||
|
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||||
|
.map((entry: ResponseCacheEntry) => {
|
||||||
|
if (hasValue((entry.response as DSOSuccessResponse).pageInfo)) {
|
||||||
|
const resPageInfo = (entry.response as DSOSuccessResponse).pageInfo;
|
||||||
|
if (isNotEmpty(resPageInfo) && resPageInfo.currentPage >= 0) {
|
||||||
|
return Object.assign({}, resPageInfo, { currentPage: resPageInfo.currentPage + 1 });
|
||||||
|
} else {
|
||||||
|
return resPageInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const payloadObs = Observable.combineLatest(tDomainListObs, pageInfoObs, (tDomainList, pageInfo) => {
|
||||||
|
if (hasValue(pageInfo)) {
|
||||||
|
return new PaginatedList(pageInfo, tDomainList);
|
||||||
|
} else {
|
||||||
|
return tDomainList;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return this.toRemoteDataObservable(hrefObs, requestObs, responseCacheObs, payloadObs);
|
return this.toRemoteDataObservable(hrefObs, requestObs, responseCacheObs, payloadObs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,35 +218,32 @@ export class RemoteDataBuildService {
|
|||||||
.every((b: boolean) => b === true);
|
.every((b: boolean) => b === true);
|
||||||
|
|
||||||
const errorMessage: string = arr
|
const errorMessage: string = arr
|
||||||
.map((d: RemoteData<T>) => d.errorMessage)
|
.map((d: RemoteData<T>) => d.error)
|
||||||
.map((e: string, idx: number) => {
|
.map((e: RemoteDataError, idx: number) => {
|
||||||
if (hasValue(e)) {
|
if (hasValue(e)) {
|
||||||
return `[${idx}]: ${e}`;
|
return `[${idx}]: ${e.message}`;
|
||||||
}
|
}
|
||||||
}).filter((e: string) => hasValue(e))
|
}).filter((e: string) => hasValue(e))
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
const statusCode: string = arr
|
const statusCode: string = arr
|
||||||
.map((d: RemoteData<T>) => d.statusCode)
|
.map((d: RemoteData<T>) => d.error)
|
||||||
.map((c: string, idx: number) => {
|
.map((e: RemoteDataError, idx: number) => {
|
||||||
if (hasValue(c)) {
|
if (hasValue(e)) {
|
||||||
return `[${idx}]: ${c}`;
|
return `[${idx}]: ${e.statusCode}`;
|
||||||
}
|
}
|
||||||
}).filter((c: string) => hasValue(c))
|
}).filter((c: string) => hasValue(c))
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
const pageInfo = undefined;
|
const error = new RemoteDataError(statusCode, errorMessage);
|
||||||
|
|
||||||
const payload: T[] = arr.map((d: RemoteData<T>) => d.payload);
|
const payload: T[] = arr.map((d: RemoteData<T>) => d.payload);
|
||||||
|
|
||||||
return new RemoteData(
|
return new RemoteData(
|
||||||
`dspace-angular://aggregated/object/${new Date().getTime()}`,
|
|
||||||
requestPending,
|
requestPending,
|
||||||
responsePending,
|
responsePending,
|
||||||
isSuccessFul,
|
isSuccessFul,
|
||||||
errorMessage,
|
error,
|
||||||
statusCode,
|
|
||||||
pageInfo,
|
|
||||||
payload
|
payload
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@@ -10,6 +10,7 @@ import { DSpaceObject } from '../shared/dspace-object.model';
|
|||||||
import { GenericConstructor } from '../shared/generic-constructor';
|
import { GenericConstructor } from '../shared/generic-constructor';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
|
import { PaginatedList } from './paginated-list';
|
||||||
import { RemoteData } from './remote-data';
|
import { RemoteData } from './remote-data';
|
||||||
import {
|
import {
|
||||||
FindAllOptions,
|
FindAllOptions,
|
||||||
@@ -70,7 +71,7 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(options: FindAllOptions = {}): Observable<RemoteData<TDomain[]>> {
|
findAll(options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<TDomain>>> {
|
||||||
const hrefObs = this.getEndpoint().filter((href: string) => isNotEmpty(href))
|
const hrefObs = this.getEndpoint().filter((href: string) => isNotEmpty(href))
|
||||||
.flatMap((endpoint: string) => this.getFindAllHref(endpoint, options));
|
.flatMap((endpoint: string) => this.getFindAllHref(endpoint, options));
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
|
|||||||
this.requestService.configure(request);
|
this.requestService.configure(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.rdbService.buildList<TNormalized, TDomain>(hrefObs, this.normalizedResourceType);
|
return this.rdbService.buildList<TNormalized, TDomain>(hrefObs, this.normalizedResourceType) as Observable<RemoteData<PaginatedList<TDomain>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFindByIDHref(endpoint, resourceID): string {
|
getFindByIDHref(endpoint, resourceID): string {
|
||||||
|
42
src/app/core/data/paginated-list.ts
Normal file
42
src/app/core/data/paginated-list.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
|
|
||||||
|
export class PaginatedList<T> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private pageInfo: PageInfo,
|
||||||
|
public page: T[]
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get elementsPerPage(): number {
|
||||||
|
return this.pageInfo.elementsPerPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
set elementsPerPage(value: number) {
|
||||||
|
this.pageInfo.elementsPerPage = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalElements(): number {
|
||||||
|
return this.pageInfo.totalElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
set totalElements(value: number) {
|
||||||
|
this.pageInfo.totalElements = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalPages(): number {
|
||||||
|
return this.pageInfo.totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
set totalPages(value: number) {
|
||||||
|
this.pageInfo.totalPages = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentPage(): number {
|
||||||
|
return this.pageInfo.currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
set currentPage(value: number) {
|
||||||
|
this.pageInfo.currentPage = value;
|
||||||
|
}
|
||||||
|
}
|
7
src/app/core/data/remote-data-error.ts
Normal file
7
src/app/core/data/remote-data-error.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export class RemoteDataError {
|
||||||
|
constructor(
|
||||||
|
public statusCode: string,
|
||||||
|
public message: string
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
import { PageInfo } from '../shared/page-info.model';
|
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
import { RemoteDataError } from './remote-data-error';
|
||||||
|
|
||||||
export enum RemoteDataState {
|
export enum RemoteDataState {
|
||||||
RequestPending = 'RequestPending',
|
RequestPending = 'RequestPending',
|
||||||
@@ -13,13 +13,10 @@ export enum RemoteDataState {
|
|||||||
*/
|
*/
|
||||||
export class RemoteData<T> {
|
export class RemoteData<T> {
|
||||||
constructor(
|
constructor(
|
||||||
public self: string,
|
|
||||||
private requestPending: boolean,
|
private requestPending: boolean,
|
||||||
private responsePending: boolean,
|
private responsePending: boolean,
|
||||||
private isSuccessFul: boolean,
|
private isSuccessFul: boolean,
|
||||||
public errorMessage: string,
|
public error: RemoteDataError,
|
||||||
public statusCode: string,
|
|
||||||
public pageInfo: PageInfo,
|
|
||||||
public payload: T
|
public payload: T
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@@ -9,14 +9,24 @@ import { BrowseResponseParsingService } from './browse-response-parsing.service'
|
|||||||
import { ConfigResponseParsingService } from './config-response-parsing.service';
|
import { ConfigResponseParsingService } from './config-response-parsing.service';
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Request Method.
|
||||||
|
*
|
||||||
|
* I didn't reuse the RequestMethod enum in @angular/http because
|
||||||
|
* it uses numbers. The string values here are more clear when
|
||||||
|
* debugging.
|
||||||
|
*
|
||||||
|
* The ones commented out are still unsupported in the rest of the codebase
|
||||||
|
*/
|
||||||
export enum RestRequestMethod {
|
export enum RestRequestMethod {
|
||||||
Get = 'GET',
|
Get = 'GET',
|
||||||
Post = 'POST',
|
Post = 'POST',
|
||||||
Put = 'PUT',
|
// Put = 'PUT',
|
||||||
Delete = 'DELETE',
|
// Delete = 'DELETE',
|
||||||
Options = 'OPTIONS',
|
// Options = 'OPTIONS',
|
||||||
Head = 'HEAD',
|
// Head = 'HEAD',
|
||||||
Patch = 'PATCH'
|
// Patch = 'PATCH'
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RestRequest {
|
export class RestRequest {
|
||||||
|
@@ -12,7 +12,7 @@ import { ResponseCacheService } from '../cache/response-cache.service';
|
|||||||
import { coreSelector, CoreState } from '../core.reducers';
|
import { coreSelector, CoreState } from '../core.reducers';
|
||||||
import { keySelector } from '../shared/selectors';
|
import { keySelector } from '../shared/selectors';
|
||||||
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
|
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest, RestRequestMethod } from './request.models';
|
||||||
|
|
||||||
import { RequestEntry, RequestState } from './request.reducer';
|
import { RequestEntry, RequestState } from './request.reducer';
|
||||||
|
|
||||||
@@ -30,11 +30,9 @@ export function requestStateSelector(): MemoizedSelector<CoreState, RequestState
|
|||||||
export class RequestService {
|
export class RequestService {
|
||||||
private requestsOnTheirWayToTheStore: string[] = [];
|
private requestsOnTheirWayToTheStore: string[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(private objectCache: ObjectCacheService,
|
||||||
private objectCache: ObjectCacheService,
|
private responseCache: ResponseCacheService,
|
||||||
private responseCache: ResponseCacheService,
|
private store: Store<CoreState>) {
|
||||||
private store: Store<CoreState>
|
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isPending(href: string): boolean {
|
isPending(href: string): boolean {
|
||||||
@@ -59,6 +57,12 @@ export class RequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configure<T extends CacheableObject>(request: RestRequest): void {
|
configure<T extends CacheableObject>(request: RestRequest): void {
|
||||||
|
if (request.method !== RestRequestMethod.Get || !this.isCachedOrPending(request)) {
|
||||||
|
this.dispatchRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isCachedOrPending(request: RestRequest) {
|
||||||
let isCached = this.objectCache.hasBySelfLink(request.href);
|
let isCached = this.objectCache.hasBySelfLink(request.href);
|
||||||
if (!isCached && this.responseCache.has(request.href)) {
|
if (!isCached && this.responseCache.has(request.href)) {
|
||||||
const [successResponse, errorResponse] = this.responseCache.get(request.href)
|
const [successResponse, errorResponse] = this.responseCache.get(request.href)
|
||||||
@@ -84,11 +88,13 @@ export class RequestService {
|
|||||||
|
|
||||||
const isPending = this.isPending(request.href);
|
const isPending = this.isPending(request.href);
|
||||||
|
|
||||||
if (!(isCached || isPending)) {
|
return isCached || isPending;
|
||||||
this.store.dispatch(new RequestConfigureAction(request));
|
}
|
||||||
this.store.dispatch(new RequestExecuteAction(request.href));
|
|
||||||
this.trackRequestsOnTheirWayToTheStore(request.href);
|
private dispatchRequest(request: RestRequest) {
|
||||||
}
|
this.store.dispatch(new RequestConfigureAction(request));
|
||||||
|
this.store.dispatch(new RequestExecuteAction(request.href));
|
||||||
|
this.trackRequestsOnTheirWayToTheStore(request.href);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -11,6 +11,7 @@ import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
|||||||
import { Store, StoreModule } from '@ngrx/store';
|
import { Store, StoreModule } from '@ngrx/store';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { RemoteDataError } from '../data/remote-data-error';
|
||||||
|
|
||||||
import { MetadataService } from './metadata.service';
|
import { MetadataService } from './metadata.service';
|
||||||
|
|
||||||
@@ -178,13 +179,10 @@ describe('MetadataService', () => {
|
|||||||
|
|
||||||
const mockRemoteData = (mockItem: Item): Observable<RemoteData<Item>> => {
|
const mockRemoteData = (mockItem: Item): Observable<RemoteData<Item>> => {
|
||||||
return Observable.of(new RemoteData<Item>(
|
return Observable.of(new RemoteData<Item>(
|
||||||
'',
|
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
'',
|
new RemoteDataError('200', ''),
|
||||||
'200',
|
|
||||||
{} as PageInfo,
|
|
||||||
MockItem
|
MockItem
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<ds-pagination
|
<ds-pagination
|
||||||
[paginationOptions]="config"
|
[paginationOptions]="config"
|
||||||
[pageInfoState]="pageInfo"
|
[pageInfoState]="objects?.payload"
|
||||||
[collectionSize]="pageInfo?.totalElements"
|
[collectionSize]="objects?.payload?.totalElements"
|
||||||
[sortOptions]="sortConfig"
|
[sortOptions]="sortConfig"
|
||||||
[hideGear]="hideGear"
|
[hideGear]="hideGear"
|
||||||
[hidePagerWhenSinglePage]="hidePagerWhenSinglePage"
|
[hidePagerWhenSinglePage]="hidePagerWhenSinglePage"
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
(sortFieldChange)="onSortFieldChange($event)"
|
(sortFieldChange)="onSortFieldChange($event)"
|
||||||
(paginationChange)="onPaginationChange($event)">
|
(paginationChange)="onPaginationChange($event)">
|
||||||
<ul *ngIf="objects?.hasSucceeded"> <!--class="list-unstyled"-->
|
<ul *ngIf="objects?.hasSucceeded"> <!--class="list-unstyled"-->
|
||||||
<li *ngFor="let object of objects?.payload">
|
<li *ngFor="let object of objects?.payload?.page">
|
||||||
<ds-wrapper-list-element [object]="object"></ds-wrapper-list-element>
|
<ds-wrapper-list-element [object]="object"></ds-wrapper-list-element>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -8,13 +8,12 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
|
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { PageInfo } from '../core/shared/page-info.model';
|
import { ListableObject } from './listable-object/listable-object.model';
|
||||||
import { ListableObject } from '../object-list/listable-object/listable-object.model';
|
|
||||||
|
|
||||||
import { fadeIn } from '../shared/animations/fade';
|
import { fadeIn } from '../shared/animations/fade';
|
||||||
import { hasValue } from '../shared/empty.util';
|
|
||||||
|
|
||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
|
|
||||||
@@ -32,13 +31,9 @@ export class ObjectListComponent {
|
|||||||
@Input() sortConfig: SortOptions;
|
@Input() sortConfig: SortOptions;
|
||||||
@Input() hideGear = false;
|
@Input() hideGear = false;
|
||||||
@Input() hidePagerWhenSinglePage = true;
|
@Input() hidePagerWhenSinglePage = true;
|
||||||
private _objects: RemoteData<ListableObject[]>;
|
private _objects: RemoteData<PaginatedList<ListableObject>>;
|
||||||
pageInfo: PageInfo;
|
@Input() set objects(objects: RemoteData<PaginatedList<ListableObject>>) {
|
||||||
@Input() set objects(objects: RemoteData<ListableObject[]>) {
|
|
||||||
this._objects = objects;
|
this._objects = objects;
|
||||||
if (hasValue(objects)) {
|
|
||||||
this.pageInfo = objects.pageInfo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
get objects() {
|
get objects() {
|
||||||
return this._objects;
|
return this._objects;
|
||||||
|
Reference in New Issue
Block a user