Skip to content
Snippets Groups Projects
Commit 1a3742d2 authored by Lotte Hofstede's avatar Lotte Hofstede
Browse files

44024: search page almost finished

parent c603eddd
Branches
Tags
No related merge requests found
Showing
with 124 additions and 95 deletions
......@@ -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,
protected responseCache: ResponseCacheService,
protected requestService: RequestService,
protected store: Store<CoreState>,
) {
constructor(protected objectCache: ObjectCacheService,
protected responseCache: ResponseCacheService,
protected requestService: RequestService,
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));
......@@ -240,7 +242,7 @@ export class RemoteDataBuildService {
})
.filter((e) => hasValue(e))
.join(', ')
);
);
const statusCode = Observable.combineLatest(
...input.map((rd) => rd.statusCode),
......@@ -252,7 +254,7 @@ export class RemoteDataBuildService {
})
.filter((c) => hasValue(c))
.join(', ')
);
);
const pageInfo = Observable.of(undefined);
......
<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>
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.
......@@ -13,30 +14,57 @@ import { DSpaceObject } from '../core/shared/dspace-object.model';
*/
@Component({
selector: 'ds-search-page',
styleUrls: ['./search-page.component.scss'],
templateUrl: './search-page.component.html',
selector: 'ds-search-page',
styleUrls: ['./search-page.component.scss'],
templateUrl: './search-page.component.html',
})
export class SearchPageComponent implements OnInit {
private sub;
results: RemoteData<Array<SearchResult<DSpaceObject>>>;
export class SearchPageComponent implements OnInit, OnDestroy {
private sub;
private query: string;
private scope: string;
private page: number;
private results: RemoteData<Array<SearchResult<DSpaceObject>>>;
private currentParams = {};
constructor(
private service: SearchService,
private route: ActivatedRoute,
) { }
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()});
ngOnInit(): void {
this.sub = this.route
.queryParams
.subscribe((params) => {
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();
}
}
);
}
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
}
)
})
;
}
}
<ds-object-list [objects]="searchResults"></ds-object-list>
\ No newline at end of file
<ds-object-list [config]="searchConfig.pagination" [sortConfig]="searchConfig.sort"
[objects]="searchResults" [hideGear]="false"></ds-object-list>
\ No newline at end of file
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();
}
}
import { SortOptions } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
export class SearchOptions {
elementsPerPage?: number;
currentPage?: number;
pagination?: PaginationComponentOptions;
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>
......
:host {
.dropdown-toggle::after {
display: none;
}
}
\ No newline at end of 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
......
<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>
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.
......@@ -8,21 +7,18 @@ import { FormGroup, FormControl } from '@angular/forms';
*/
@Component({
selector: 'ds-search-form',
styleUrls: ['./search-form.component.scss'],
templateUrl: './search-form.component.html',
selector: 'ds-search-form',
styleUrls: ['./search-form.component.scss'],
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);
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment