mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 10:34:15 +00:00
44024: search page almost finished
This commit is contained in:
@@ -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));
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
}
|
||||
)
|
||||
})
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
@@ -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>>>;
|
||||
|
||||
@Input() searchConfig: SearchOptions;
|
||||
|
||||
ngOnInit(): void {
|
||||
// onInit
|
||||
this.searchConfig = new SearchOptions();
|
||||
this.searchConfig.pagination = new PaginationComponentOptions();
|
||||
this.searchConfig.pagination.id = 'search-results-pagination';
|
||||
this.searchConfig.sort = new SortOptions();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
7
src/app/search/search-options.model.ts
Normal file
7
src/app/search/search-options.model.ts
Normal 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;
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
import { SortOptions } from '../core/cache/models/sort-options.model';
|
||||
|
||||
export class SearchOptions {
|
||||
elementsPerPage?: number;
|
||||
currentPage?: number;
|
||||
sort?: SortOptions;
|
||||
}
|
@@ -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,
|
||||
|
@@ -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>
|
||||
|
5
src/app/shared/pagination/pagination.component.scss
Normal file
5
src/app/shared/pagination/pagination.component.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
:host {
|
||||
.dropdown-toggle::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user