small fixes for PR

This commit is contained in:
lotte
2018-08-03 17:39:47 +02:00
parent 11f4befceb
commit e7ea6294d7
13 changed files with 135 additions and 29 deletions

View File

@@ -87,7 +87,8 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
const facetValues = Observable.combineLatest(searchOptions, this.currentPage, (options, page) => {
return { options, page }
}).switchMap(({ options, page }) => {
return this.searchService.getFacetValuesFor(this.filterConfig, page, options).map((results) => {
return this.searchService.getFacetValuesFor(this.filterConfig, page, options)
.first((RD) => !RD.isLoading).map((results) => {
return {
values: Observable.of(results),
page: page

View File

@@ -1,7 +1,7 @@
<h3>{{"search.filters.head" | translate}}</h3>
<div *ngIf="(filters | async)?.hasSucceeded">
<div *ngFor="let filter of (filters | async)?.payload">
<ds-search-filter class="d-block mb-3 p-3" [filter]="filter"></ds-search-filter>
<ds-search-filter *ngIf="isActive(filter) | async" class="d-block mb-3 p-3" [filter]="filter"></ds-search-filter>
</div>
</div>
<a class="btn btn-primary" [routerLink]="[getSearchLink()]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button">{{"search.filters.reset" | translate}}</a>

View File

@@ -24,6 +24,12 @@ describe('SearchFiltersComponent', () => {
}
/* tslint:enable:no-empty */
};
const searchFiltersStub = {
getSelectedValuesForFilter: (filter) =>
[]
};
const searchConfigServiceStub = jasmine.createSpyObj('SearchConfigurationService', {
getCurrentFrontendFilters: Observable.of({})
});
@@ -35,6 +41,7 @@ describe('SearchFiltersComponent', () => {
providers: [
{ provide: SearchService, useValue: searchServiceStub },
{ provide: SearchConfigurationService, useValue: searchConfigServiceStub },
{ provide: SearchFilterService, useValue: searchFiltersStub },
],
schemas: [NO_ERRORS_SCHEMA]

View File

@@ -4,6 +4,8 @@ import { RemoteData } from '../../core/data/remote-data';
import { SearchFilterConfig } from '../search-service/search-filter-config.model';
import { Observable } from 'rxjs/Observable';
import { SearchConfigurationService } from '../search-service/search-configuration.service';
import { isNotEmpty } from '../../shared/empty.util';
import { SearchFilterService } from './search-filter/search-filter.service';
@Component({
selector: 'ds-search-filters',
@@ -30,10 +32,14 @@ export class SearchFiltersComponent {
* Initialize instance variables
* @param {SearchService} searchService
* @param {SearchConfigurationService} searchConfigService
* @param {SearchFilterService} filterService
*/
constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService) {
this.filters = searchService.getConfig();
this.clearParams = searchConfigService.getCurrentFrontendFilters().map((filters) => {Object.keys(filters).forEach((f) => filters[f] = null); return filters;});
constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) {
this.filters = searchService.getConfig().first((RD) => !RD.isLoading);
this.clearParams = searchConfigService.getCurrentFrontendFilters().map((filters) => {
Object.keys(filters).forEach((f) => filters[f] = null);
return filters;
});
}
/**
@@ -42,4 +48,29 @@ export class SearchFiltersComponent {
getSearchLink() {
return this.searchService.getSearchLink();
}
/**
* Check if a given filter is supposed to be shown or not
* @param {SearchFilterConfig} filter The filter to check for
* @returns {Observable<boolean>} Emits true whenever a given filter config should be shown
*/
isActive(filter: SearchFilterConfig): Observable<boolean> {
// console.log(filter.name);
return this.filterService.getSelectedValuesForFilter(filter)
.flatMap((isActive) => {
if (isNotEmpty(isActive)) {
return Observable.of(true);
} else {
return this.searchConfigService.searchOptions
.switchMap((options) => {
return this.searchService.getFacetValuesFor(filter, 1, options)
.filter((RD) => !RD.isLoading)
.map((valuesRD) => {
return valuesRD.payload.totalElements > 0
})
}
)
}
}).startWith(true);
}
}

View File

@@ -1,12 +1,13 @@
<div class="row">
<div class="row mb-3 mb-md-1">
<div class="labels col-sm-9 offset-sm-3">
<ng-container *ngFor="let key of ((appliedFilters | async) | dsObjectKeys)">
<a *ngFor="let values of (appliedFilters | async)[key]" class="badge badge-primary mr-1"
<ng-container *ngFor="let key of ((appliedFilters | async) | dsObjectKeys)"><!--Do not remove this to prevent uneven spacing
--><a *ngFor="let values of (appliedFilters | async)[key]"
class="badge badge-primary mr-1 mb-1"
[routerLink]="getSearchLink()"
[queryParams]="(getRemoveParams(key, values) | async)" queryParamsHandling="merge">
{{('search.filters.applied.' + key) | translate}}: {{values}}
<span> ×</span>
</a>
</ng-container>
</a><!--Do not remove this to prevent uneven spacing
--></ng-container>
</div>
</div>

View File

@@ -0,0 +1,3 @@
:host {
line-height: 1;
}

View File

@@ -8,6 +8,7 @@ import { SearchConfigurationService } from '../search-service/search-configurati
@Component({
selector: 'ds-search-labels',
styleUrls: ['./search-labels.component.scss'],
templateUrl: './search-labels.component.html',
})

View File

@@ -16,24 +16,55 @@ import { Subscription } from 'rxjs/Subscription';
*/
@Injectable()
export class SearchConfigurationService implements OnDestroy {
/**
* Default pagination settings
*/
private defaultPagination = Object.assign(new PaginationComponentOptions(), {
id: 'search-page-configuration',
pageSize: 10,
currentPage: 1
});
/**
* Default sort settings
*/
private defaultSort = new SortOptions('score', SortDirection.DESC);
/**
* Default scope setting
*/
private defaultScope = '';
/**
* Default query setting
*/
private defaultQuery = '';
private _defaults;
/**
* Emits the current default values
*/
private _defaults: Observable<RemoteData<PaginatedSearchOptions>>;
/**
* Emits the current search options
*/
public searchOptions: BehaviorSubject<SearchOptions>;
/**
* Emits the current search options including pagination and sort
*/
public paginatedSearchOptions: BehaviorSubject<PaginatedSearchOptions>;
/**
* List of subscriptions to unsubscribe from on destroy
*/
private subs: Subscription[] = new Array();
/**
* Initialize the search options
* @param {RouteService} routeService
* @param {ActivatedRoute} route
*/
constructor(private routeService: RouteService,
private route: ActivatedRoute) {
this.defaults.first().subscribe((defRD) => {
@@ -128,6 +159,11 @@ export class SearchConfigurationService implements OnDestroy {
return this.routeService.getQueryParamsWithPrefix('f.');
}
/**
* Sets up a subscription to all necessary parameters to make sure the searchOptions emits a new value every time they update
* @param {SearchOptions} defaults Default values for when no parameters are available
* @returns {Subscription} The subscription to unsubscribe from
*/
subscribeToSearchOptions(defaults: SearchOptions): Subscription {
return Observable.merge(
this.getScopePart(defaults.scope),
@@ -140,6 +176,11 @@ export class SearchConfigurationService implements OnDestroy {
});
}
/**
* Sets up a subscription to all necessary parameters to make sure the paginatedSearchOptions emits a new value every time they update
* @param {PaginatedSearchOptions} defaults Default values for when no parameters are available
* @returns {Subscription} The subscription to unsubscribe from
*/
subscribeToPaginatedSearchOptions(defaults: PaginatedSearchOptions): Subscription {
return Observable.merge(
this.getPaginationPart(defaults.pagination),
@@ -170,12 +211,18 @@ export class SearchConfigurationService implements OnDestroy {
return this._defaults;
}
/**
* Make sure to unsubscribe from all existing subscription to prevent memory leaks
*/
ngOnDestroy(): void {
this.subs.forEach((sub) => {
sub.unsubscribe();
});
}
/**
* @returns {Observable<string>} Emits the current scope's identifier
*/
private getScopePart(defaultScope: string): Observable<any> {
return this.getCurrentScope(defaultScope).map((scope) => {
return { scope }
@@ -183,7 +230,7 @@ export class SearchConfigurationService implements OnDestroy {
}
/**
* @returns {Observable<string>} Emits the current query string
* @returns {Observable<string>} Emits the current query string as a partial SearchOptions object
*/
private getQueryPart(defaultQuery: string): Observable<any> {
return this.getCurrentQuery(defaultQuery).map((query) => {
@@ -192,7 +239,7 @@ export class SearchConfigurationService implements OnDestroy {
}
/**
* @returns {Observable<string>} Emits the current pagination settings
* @returns {Observable<string>} Emits the current pagination settings as a partial SearchOptions object
*/
private getPaginationPart(defaultPagination: PaginationComponentOptions): Observable<any> {
return this.getCurrentPagination(defaultPagination).map((pagination) => {
@@ -201,7 +248,7 @@ export class SearchConfigurationService implements OnDestroy {
}
/**
* @returns {Observable<string>} Emits the current sorting settings
* @returns {Observable<string>} Emits the current sorting settings as a partial SearchOptions object
*/
private getSortPart(defaultSort: SortOptions): Observable<any> {
return this.getCurrentSort(defaultSort).map((sort) => {
@@ -210,7 +257,7 @@ 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 as a partial SearchOptions object
*/
private getFiltersPart(): Observable<any> {
return this.getCurrentFilters().map((filters) => {

View File

@@ -8,7 +8,7 @@ export class PaginatedList<T> {
}
get elementsPerPage(): number {
if (hasValue(this.pageInfo)) {
if (hasValue(this.pageInfo) && hasValue(this.pageInfo.elementsPerPage)) {
return this.pageInfo.elementsPerPage;
}
return this.page.length;
@@ -19,7 +19,7 @@ export class PaginatedList<T> {
}
get totalElements(): number {
if (hasValue(this.pageInfo)) {
if (hasValue(this.pageInfo) && hasValue(this.pageInfo.totalElements)) {
return this.pageInfo.totalElements;
}
return this.page.length;
@@ -30,7 +30,7 @@ export class PaginatedList<T> {
}
get totalPages(): number {
if (hasValue(this.pageInfo)) {
if (hasValue(this.pageInfo) && hasValue(this.pageInfo.totalPages)) {
return this.pageInfo.totalPages;
}
return 1;
@@ -41,7 +41,7 @@ export class PaginatedList<T> {
}
get currentPage(): number {
if (hasValue(this.pageInfo)) {
if (hasValue(this.pageInfo) && hasValue(this.pageInfo.currentPage)) {
return this.pageInfo.currentPage;
}
return 1;

View File

@@ -1,15 +1,13 @@
import {
RegistryMetadatafieldsSuccessResponse, RegistryMetadataschemasSuccessResponse,
RegistryMetadatafieldsSuccessResponse,
RestResponse
} from '../cache/response-cache.models';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { RestRequest } from './request.models';
import { ResponseParsingService } from './parsing.service';
import { RegistryMetadataschemasResponse } from '../registry/registry-metadataschemas-response.model';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { DSOResponseParsingService } from './dso-response-parsing.service';
import { Injectable } from '@angular/core';
import { forEach } from '@angular/router/src/utils/collection';
import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-response.model';
@Injectable()

View File

@@ -5,7 +5,7 @@
(dsClickOutside)="close()">
<input #inputField type="text" [(ngModel)]="ngModel" [name]="name"
class="form-control suggestion_input"
[dsDebounce]="debounceTime" (onDebounce)="findSuggestions.emit($event)"
[dsDebounce]="debounceTime" (onDebounce)="find($event)"
[placeholder]="placeholder"
[ngModelOptions]="{standalone: true}" autocomplete="off"/>
<input type="submit" class="d-none"/>

View File

@@ -80,6 +80,11 @@ export class InputSuggestionsComponent {
*/
selectedIndex = -1;
/**
* True when the dropdown should not reopen
*/
blockReopen = false;
/**
* Reference to the input field component
*/
@@ -162,13 +167,25 @@ export class InputSuggestionsComponent {
}
/**
* Make sure that if a suggestion is clicked, the suggestions dropdown closes and the focus moves to the input field
* Make sure that if a suggestion is clicked, the suggestions dropdown closes, does not reopen and the focus moves to the input field
*/
onClickSuggestion(data) {
this.clickSuggestion.emit(data);
this.close();
this.blockReopen = true;
this.queryInput.nativeElement.focus();
return false;
}
/**
* Finds new suggestions when necessary
* @param data The query value to emit
*/
find(data) {
if (!this.blockReopen) {
this.findSuggestions.emit(data);
}
this.blockReopen = false;
}
}

View File

@@ -1,7 +1,7 @@
import { Component, Input } from '@angular/core';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { Router } from '@angular/router';
import { isNotEmpty, hasValue, isEmpty, hasNoValue } from '../empty.util';
import { hasValue, isNotEmpty } from '../empty.util';
import { QueryParamsHandling } from '@angular/router/src/config';
/**