44024: search page almost finished

This commit is contained in:
Lotte Hofstede
2017-08-24 16:39:40 +02:00
parent c603eddd52
commit 1a3742d23a
13 changed files with 124 additions and 102 deletions

View File

@@ -16,21 +16,18 @@ import { GenericConstructor } from '../../shared/generic-constructor';
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
import { NormalizedObjectFactory } from '../models/normalized-object-factory';
import { Request } from '../../data/request.models';
import { PageInfo } from '../../shared/page-info.model';
@Injectable()
export class RemoteDataBuildService {
constructor(
protected objectCache: ObjectCacheService,
constructor(protected objectCache: ObjectCacheService,
protected responseCache: ResponseCacheService,
protected requestService: RequestService,
protected store: Store<CoreState>,
) {
protected store: Store<CoreState>,) {
}
buildSingle<TNormalized extends CacheableObject, TDomain>(
href: string,
normalizedType: GenericConstructor<TNormalized>
): RemoteData<TDomain> {
buildSingle<TNormalized extends CacheableObject, TDomain>(href: string,
normalizedType: GenericConstructor<TNormalized>): RemoteData<TDomain> {
const requestHrefObs = this.objectCache.getRequestHrefBySelfLink(href);
const requestObs = Observable.race(
@@ -64,6 +61,13 @@ export class RemoteDataBuildService {
const pageInfo = responseCacheObs
.filter((entry: ResponseCacheEntry) => hasValue(entry.response) && hasValue(entry.response['pageInfo']))
.map((entry: ResponseCacheEntry) => (entry.response as SuccessResponse).pageInfo)
.map((pInfo: PageInfo) => {
if (isNotEmpty(pageInfo) && pInfo.currentPage >= 0) {
return Object.assign({}, pInfo, {currentPage: pInfo.currentPage + 1});
} else {
return pInfo;
}
})
.distinctUntilChanged();
/* tslint:enable:no-string-literal */
@@ -107,10 +111,8 @@ export class RemoteDataBuildService {
);
}
buildList<TNormalized extends CacheableObject, TDomain>(
href: string,
normalizedType: GenericConstructor<TNormalized>
): RemoteData<TDomain[]> {
buildList<TNormalized extends CacheableObject, TDomain>(href: string,
normalizedType: GenericConstructor<TNormalized>): RemoteData<TDomain[]> {
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href)
.filter((entry) => hasValue(entry));
const responseCacheObs = this.responseCache.get(href).filter((entry) => hasValue(entry));

View File

@@ -1,4 +1,4 @@
<div class="search-page">
<ds-search-form></ds-search-form>
<ds-search-form (formSubmit)="updateSearch($event)" [query]="query"></ds-search-form>
<ds-search-results [searchResults]="results"></ds-search-results>
</div>

View File

@@ -1,10 +1,11 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SearchService } from '../search/search.service';
import { ActivatedRoute } from '@angular/router';
import { SortOptions } from '../core/cache/models/sort-options.model';
import { ActivatedRoute, Router } from '@angular/router';
import { RemoteData } from '../core/data/remote-data';
import { SearchResult } from '../search/search-result.model';
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { SortOptions } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
/**
* This component renders a simple item page.
@@ -17,26 +18,53 @@ import { DSpaceObject } from '../core/shared/dspace-object.model';
styleUrls: ['./search-page.component.scss'],
templateUrl: './search-page.component.html',
})
export class SearchPageComponent implements OnInit {
export class SearchPageComponent implements OnInit, OnDestroy {
private sub;
results: RemoteData<Array<SearchResult<DSpaceObject>>>;
private query: string;
private scope: string;
private page: number;
private results: RemoteData<Array<SearchResult<DSpaceObject>>>;
private currentParams = {};
constructor(
private service: SearchService,
constructor(private service: SearchService,
private route: ActivatedRoute,
) { }
private router: Router,) {
}
ngOnInit(): void {
this.sub = this.route
.queryParams
.subscribe((params) => {
const query: string = params.query || '';
const scope: string = params.scope;
const page: number = +params.page || 0;
this.results = this.service.search(query, scope, {elementsPerPage: 10, currentPage: page, sort: new SortOptions()});
this.currentParams = params;
this.query = params.query || '';
this.scope = params.scope;
this.page = +params.page || 1;
const pagination: PaginationComponentOptions = new PaginationComponentOptions();
pagination.id = 'results-pagination';
pagination.currentPage = this.page;
pagination.pageSize = +params.pageSize;
this.results = this.service.search(this.query, this.scope, {
pagination: pagination,
sort: new SortOptions(params.sortField, params.sortDirection)
});
}
);
}
ngOnDestroy() {
this.sub.unsubscribe();
}
updateSearch(data: any) {
this.router.navigate([], {
queryParams: Object.assign({}, this.currentParams,
{
query: data.query,
scope: data.scope,
page: data.page || 1
}
)
})
;
}
}

View File

@@ -1 +1,2 @@
<ds-object-list [objects]="searchResults"></ds-object-list>
<ds-object-list [config]="searchConfig.pagination" [sortConfig]="searchConfig.sort"
[objects]="searchResults" [hideGear]="false"></ds-object-list>

View File

@@ -1,7 +1,10 @@
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { RemoteData } from '../../core/data/remote-data';
import { SearchResult } from '../../search/search-result.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { SortOptions, SortDirection } from '../../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SearchOptions } from '../../search/search-options.model';
/**
* This component renders a simple item page.
@@ -17,8 +20,13 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
export class SearchResultsComponent implements OnInit {
@Input() searchResults: RemoteData<Array<SearchResult<DSpaceObject>>>;
ngOnInit(): void {
// onInit
@Input() searchConfig: SearchOptions;
ngOnInit(): void {
this.searchConfig = new SearchOptions();
this.searchConfig.pagination = new PaginationComponentOptions();
this.searchConfig.pagination.id = 'search-results-pagination';
this.searchConfig.sort = new SortOptions();
}
}

View File

@@ -0,0 +1,7 @@
import { SortOptions } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
export class SearchOptions {
pagination?: PaginationComponentOptions;
sort?: SortOptions;
}

View File

@@ -1,7 +0,0 @@
import { SortOptions } from '../core/cache/models/sort-options.model';
export class SearchOptions {
elementsPerPage?: number;
currentPage?: number;
sort?: SortOptions;
}

View File

@@ -5,7 +5,7 @@ import { SearchResult } from './search-result.model';
import { ItemDataService } from '../core/data/item-data.service';
import { PageInfo } from '../core/shared/page-info.model';
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { SearchOptions } from './search.models';
import { SearchOptions } from './search-options.model';
import { hasValue, isNotEmpty } from '../shared/empty.util';
import { Metadatum } from '../core/shared/metadatum.model';
import { Item } from '../core/shared/item.model';
@@ -36,19 +36,18 @@ export class SearchService {
if (hasValue(scopeId)) {
self += `&scope=${scopeId}`;
}
if (isNotEmpty(searchOptions) && hasValue(searchOptions.currentPage)) {
self += `&page=${searchOptions.currentPage}`;
if (isNotEmpty(searchOptions) && hasValue(searchOptions.pagination.currentPage)) {
self += `&page=${searchOptions.pagination.currentPage}`;
}
const requestPending = Observable.of(false);
const responsePending = Observable.of(false);
const isSuccessFul = Observable.of(true);
const errorMessage = Observable.of(undefined);
const statusCode = Observable.of('200');
const returningPageInfo = new PageInfo();
if (isNotEmpty(searchOptions)) {
returningPageInfo.elementsPerPage = searchOptions.elementsPerPage;
returningPageInfo.currentPage = searchOptions.currentPage;
returningPageInfo.elementsPerPage = searchOptions.pagination.pageSize;
returningPageInfo.currentPage = searchOptions.pagination.currentPage;
} else {
returningPageInfo.elementsPerPage = 10;
returningPageInfo.currentPage = 1;
@@ -82,7 +81,7 @@ export class SearchService {
self,
requestPending,
responsePending,
isSuccessFul,
itemsRD.hasSucceeded,
errorMessage,
statusCode,
pageInfo,

View File

@@ -7,8 +7,8 @@
</div>
<div class="col">
<div ngbDropdown #paginationControls="ngbDropdown" class="d-inline-block float-right">
<button class="btn btn-outline-primary" id="paginationControls" (click)="$event.stopPropagation(); (paginationControls.isOpen())?paginationControls.close():paginationControls.open();"><i class="fa fa-cog" aria-hidden="true"></i></button>
<div class="dropdown-menu dropdown-menu-right" id="paginationControlsDropdownMenu" aria-labelledby="paginationControls">
<button class="btn btn-outline-primary" id="paginationControls" ngbDropdownToggle><i class="fa fa-cog" aria-hidden="true"></i></button>
<div class="dropdown-menu dropdown-menu-right" id="paginationControlsDropdownMenu" aria-labelledby="paginationControls" ngbDropdownMenu>
<h6 class="dropdown-header">{{ 'pagination.results-per-page' | translate}}</h6>
<button class="dropdown-item" style="padding-left: 20px" *ngFor="let item of pageSizeOptions" (click)="setPageSize(item)"><i class="fa fa-check {{(item != paginationOptions.pageSize) ? 'invisible' : ''}}" aria-hidden="true"></i> {{item}} </button>
<h6 class="dropdown-header">{{ 'pagination.sort-direction' | translate}}</h6>

View File

@@ -0,0 +1,5 @@
:host {
.dropdown-toggle::after {
display: none;
}
}

View File

@@ -30,11 +30,12 @@ import { PageInfo } from '../../core/shared/page-info.model';
@Component({
exportAs: 'paginationComponent',
selector: 'ds-pagination',
styleUrls: ['pagination.component.scss'],
templateUrl: 'pagination.component.html',
changeDetection: ChangeDetectionStrategy.Default,
encapsulation: ViewEncapsulation.Emulated
})
export class PaginationComponent implements OnChanges, OnDestroy, OnInit {
export class PaginationComponent implements OnDestroy, OnInit {
/**
* Number of items in collection.
@@ -165,15 +166,6 @@ export class PaginationComponent implements OnChanges, OnDestroy, OnInit {
total: null
};
ngOnChanges(changes: SimpleChanges) {
if (changes.pageInfoState && !changes.pageInfoState.isFirstChange()) {
this.subs.push(this.pageInfoState.subscribe((pageInfo) => {
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
this.currentPageState = pageInfo.currentPage + 1;
}));
}
}
/**
* Method provided by Angular. Invoked after the constructor.
*/
@@ -185,13 +177,6 @@ export class PaginationComponent implements OnChanges, OnDestroy, OnInit {
}));
this.checkConfig(this.paginationOptions);
if (this.pageInfoState) {
this.subs.push(this.pageInfoState.subscribe((pageInfo) => {
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
this.currentPageState = pageInfo.currentPage + 1;
}));
}
this.id = this.paginationOptions.id || null;
this.pageSizeOptions = this.paginationOptions.pageSizeOptions;
this.subs.push(this.route.queryParams

View File

@@ -1,7 +1,6 @@
<form [formGroup]="searchFormGroup" action="/search">
<form #form="ngForm" (ngSubmit)="onSubmit(form.value)">
<div class="input-group">
<input type="text" class="form-control" aria-label="Search input">
<input type="text" [ngModel]="query" name="query" class="form-control" aria-label="Search input">
<div class="input-group-btn" ngbDropdown>
<button type="submit" class="btn btn-secondary">Search DSpace</button>
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"
@@ -9,11 +8,10 @@
<span class="sr-only">Toggle Dropdown</span>
</button>
<div ngbDropdownMenu class="dropdown-menu dropdown-menu-right" aria-labelledby="searchDropdown">
<a class="dropdown-item" href="#">Search DSpace</a>
<a class="dropdown-item" href="#">Search this Collection</a>
<a class="dropdown-item" href="#">Search this Community</a>
<a class="dropdown-item" (click)="onSubmit(form.value)">Search DSpace</a>
<a class="dropdown-item" (click)="onSubmit(form.value, '7669c72a-3f2a-451f-a3b9-9210e7a4c02f')">Search OR2017 - Demonstration</a>
<a class="dropdown-item" (click)="onSubmit(form.value, '9076bd16-e69a-48d6-9e41-0238cb40d863')">Search Sample Community</a>
</div>
</div>
</div>
</form>

View File

@@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { Component, OnInit, EventEmitter, Output, Input } from '@angular/core';
/**
* This component renders a simple item page.
@@ -13,16 +12,13 @@ import { FormGroup, FormControl } from '@angular/forms';
templateUrl: './search-form.component.html',
})
export class SearchFormComponent implements OnInit {
searchFormGroup: FormGroup;
//
// constructor() {
//
// }
//
ngOnInit(): void {
this.searchFormGroup = new FormGroup({
firstName: new FormControl()
});
}
@Output() formSubmit: EventEmitter<any> = new EventEmitter<any>();
@Input() query: string;
ngOnInit(): void { }
onSubmit(form: any, scope?: string) {
const data: any = Object.assign({}, form, { scope: scope });
this.formSubmit.emit(data);
}
}