45621: filters facet route service

This commit is contained in:
Lotte Hofstede
2017-11-09 15:02:36 +01:00
parent 57c525d178
commit 569d7a634b
14 changed files with 171 additions and 75 deletions

View File

@@ -1,7 +1,7 @@
<a *ngFor="let value of filterValues; let i=index" class="d-block" [routerLink]="[getSearchLink()]"
[queryParams]="getQueryParams(value)">
[queryParams]="getQueryParams(value) | async">
<ng-template [ngIf]="i < (facetCount | async)">
<input type="checkbox" [checked]="isChecked(value)"/>
<input type="checkbox" [checked]="isChecked(value) | async"/>
<span class="filter-value">{{value.value}}</span>
<span class="filter-value-count float-right">({{value.count}})</span>
</ng-template>

View File

@@ -30,16 +30,16 @@ export class SidebarFacetFilterComponent implements OnInit {
this.currentPage = this.filterService.getPage(this.filterConfig.name);
}
isChecked(value: FacetValue) {
return this.filterService.isFilterActive(this.filterConfig.name, value.value);
isChecked(value: FacetValue): Observable<boolean> {
return this.filterService.isFilterActive(this.filterConfig.paramName, value.value);
}
getSearchLink() {
return this.filterService.searchLink;
}
getQueryParams(value: FacetValue): Params {
return this.filterService.switchFilterInURL(this.filterConfig, value.value);
getQueryParams(value: FacetValue): Observable<Params> {
return this.filterService.getFilterValueURL(this.filterConfig, value.value);
}
get facetCount(): Observable<number> {

View File

@@ -3,6 +3,6 @@
[ngClass]="(isCollapsed() | async) ? 'fa-plus' : 'fa-minus'"></span></div>
<div [@slide]="(isCollapsed() | async) ? 'collapsed' : 'expanded'" class="search-filter-wrapper">
<ds-search-facet-filter [filterConfig]="filter"
[filterValues]="filterValues.payload | async"></ds-search-facet-filter>
[filterValues]="(filterValues | async)?.payload"></ds-search-facet-filter>
</div>
</div>

View File

@@ -22,7 +22,7 @@ import { slide } from '../../../shared/animations/slide';
export class SidebarFilterComponent implements OnInit {
@Input() filter: SearchFilterConfig;
filterValues: RemoteData<FacetValue[]>;
filterValues: Observable<RemoteData<FacetValue[]>>;
constructor(private searchService: SearchService, private filterService: SearchFilterService) {
}

View File

@@ -10,7 +10,7 @@ import {
SearchFilterToggleAction
} from './search-filter.actions';
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ActivatedRoute, convertToParamMap, Params, Router } from '@angular/router';
import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
import { RemoteData } from '../../../core/data/remote-data';
import { PageInfo } from '../../../core/shared/page-info.model';
@@ -21,55 +21,49 @@ import { SearchService } from '../../search-service/search.service';
const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
@Injectable()
export class SearchFilterService implements OnDestroy {
private sub;
export class SearchFilterService {
constructor(private store: Store<SearchFiltersState>,
private route: ActivatedRoute,
private router: Router,
private searchService: SearchService) {
this.route.queryParams.subscribe((params) => {
console.log(params);
})
}
isFilterActive(filterName: string, filterValue: string): boolean {
let filterConfig: SearchFilterConfig;
this.sub = this.searchService.getConfig().payload
.subscribe((configuration) => filterConfig = configuration
.find((config: SearchFilterConfig) => config.name === filterName));
return isNotEmpty(this.route.snapshot.queryParams[filterConfig.paramName]) && [...this.route.snapshot.queryParams[filterConfig.paramName]].indexOf(filterValue, 0) > -1;
isFilterActive(paramName: string, filterValue: string): Observable<boolean> {
return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(filterValue) > -1 );
}
switchFilterInURL(filterConfig: SearchFilterConfig, value: string) {
console.log(this.route.snapshot.queryParams);
if (this.isFilterActive(filterConfig.name, value)) {
return this.removeQueryParameter(filterConfig.paramName, value);
} else {
return this.addQueryParameter(filterConfig.paramName, value);
}
}
addQueryParameter(paramName: string, value: string): Params {
const currentParams = this.route.snapshot.queryParams;
const newParam = {};
if ((currentParams[paramName])) {
newParam[paramName] = [...currentParams[paramName], value];
} else {
newParam[paramName] = [value];
}
return Object.assign({}, currentParams, newParam);
}
removeQueryParameter(paramName: string, value: string): Params {
const currentParams = this.route.snapshot.queryParams;
const newParam = {};
let currentFilterParams = [...currentParams[paramName]];
if (isNotEmpty(currentFilterParams)) {
const index = currentFilterParams.indexOf(value, 0);
if (index > -1) {
currentFilterParams = currentFilterParams.splice(index, 1);
getFilterValueURL(filterConfig: SearchFilterConfig, value: string): Observable<Params> {
return this.isFilterActive(filterConfig.paramName, value).flatMap((isActive) => {
if (isActive) {
return this.removeQueryParameter(filterConfig.paramName, value);
} else {
return this.addQueryParameter(filterConfig.paramName, value);
}
newParam[paramName] = currentFilterParams;
}
return Object.assign({}, currentParams, newParam);
})
}
addQueryParameter(paramName: string, value: string): Observable<Params> {
return this.route.queryParams.map((currentParams) => {
const newParam = {};
newParam[paramName] = [...convertToParamMap(currentParams).getAll(paramName), value];
return Object.assign({}, currentParams, newParam);
});
}
removeQueryParameter(paramName: string, value: string): Observable<Params> {
return this.route.queryParams.map((currentParams) => {
const newParam = {};
const currentFilterParams = convertToParamMap(currentParams).getAll(paramName);
if (isNotEmpty(currentFilterParams)) {
newParam[paramName] = currentFilterParams.filter((param) => (param !== value));
}
console.log(Object.assign({}, currentParams, newParam));
return Object.assign({}, currentParams, newParam);
});
}
get searchLink() {
@@ -113,12 +107,6 @@ export class SearchFilterService implements OnDestroy {
public increasePage(filterName: string): void {
this.store.dispatch(new SearchFilterIncreasePageAction(filterName));
}
ngOnDestroy(): void {
if (this.sub !== undefined) {
this.sub.unsubscribe();
}
}
}
function filterByNameSelector(name: string): MemoizedSelector<SearchFiltersState, SearchFilterState> {

View File

@@ -1,6 +1,6 @@
<h2>{{"search.filters.head" | translate}}</h2>
<div *ngIf="filters.hasSucceeded | async">
<div *ngFor="let filter of (filters.payload | async)">
<div *ngIf="(filters | async).hasSucceeded">
<div *ngFor="let filter of (filters | async).payload">
<ds-search-filter [filter]="filter"></ds-search-filter>
</div>
</div>

View File

@@ -2,6 +2,7 @@ import { Component, Input } from '@angular/core';
import { SearchService } from '../search-service/search.service';
import { RemoteData } from '../../core/data/remote-data';
import { SearchFilterConfig } from '../search-service/search-filter-config.model';
import { Observable } from 'rxjs/Observable';
/**
* This component renders a simple item page.
@@ -16,7 +17,7 @@ import { SearchFilterConfig } from '../search-service/search-filter-config.model
})
export class SidebarFiltersComponent {
filters: RemoteData<SearchFilterConfig[]>;
filters: Observable<RemoteData<SearchFilterConfig[]>>;
constructor(private searchService: SearchService) {
this.filters = searchService.getConfig();
}

View File

@@ -2,7 +2,7 @@
<div class="search-page row">
<ds-search-sidebar *ngIf="!(isMobileView | async)" class="col-3 sidebar-sm-sticky"
id="search-sidebar"
resultCount="{{(results.pageInfo | async)?.totalElements}}"></ds-search-sidebar>
resultCount="{{(results | async)?.pageInfo.totalElements}}"></ds-search-sidebar>
<div class="col-12 col-sm-9">
<ds-search-form id="search-form"
[query]="query"
@@ -16,9 +16,14 @@
[@pushInOut]="(isSidebarCollapsed() | async) ? 'collapsed' : 'expanded'">
<ds-search-sidebar *ngIf="(isMobileView | async)" class="col-12"
id="search-sidebar-xs"
resultCount="{{(results.pageInfo | async)?.totalElements}}"
resultCount="{{(results | async)?.pageInfo.totalElements}}"
(toggleSidebar)="closeSidebar()"
[ngClass]="{'active': !(isSidebarCollapsed() | async)}"></ds-search-sidebar>
[ngClass]="{'active': !(isSidebarCollapsed() | async)}">
</ds-search-sidebar>
<ng-template [ngIf]="(isSidebarCollapsed() | async)">Not Active - Sidebar is collapsed</ng-template>
<ng-template [ngIf]="!(isSidebarCollapsed() | async)">Active- Sidebar is not collapsed</ng-template>
<div id="search-content" class="col-12">
<div class="d-block d-sm-none search-controls clearfix">
<ds-view-mode-switch></ds-view-mode-switch>

View File

@@ -1,6 +1,6 @@
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { Store } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core';
@@ -18,7 +18,7 @@ import { By } from '@angular/platform-browser';
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
import { SearchSidebarService } from './search-sidebar/search-sidebar.service';
describe('SearchPageComponent', () => {
fdescribe('SearchPageComponent', () => {
let comp: SearchPageComponent;
let fixture: ComponentFixture<SearchPageComponent>;
let searchServiceObject: SearchService;
@@ -41,10 +41,11 @@ describe('SearchPageComponent', () => {
})
};
const sidebarService = {
isCollapsed: Observable.of(true),
collapse: () => this.isCollapsed = Observable.of(true),
expand: () => this.isCollapsed = Observable.of(false)
}
collapsed: Observable.of(true),
collapse: () => {this.collapsed = Observable.of(true)},
expand: () => {console.log('expand'); this.collapsed = Observable.of(false)},
isCollapsed: () => {if (this.collapse) {this.collapsed.subscribe(a => console.log(a))};return this.collapsed},
};
const mockCommunityList = [];
const communityDataServiceStub = {
@@ -178,11 +179,12 @@ describe('SearchPageComponent', () => {
beforeEach(() => {
menu = fixture.debugElement.query(By.css('#search-sidebar-xs')).nativeElement;
comp.isSidebarCollapsed = () => Observable.of(true);
fixture.detectChanges();
sidebarService.collapse();
});
it('should close the sidebar', () => {
sidebarService.isCollapsed().subscribe((v) => console.log('after closing, is collapsed is...: ', v));
console.log(menu.classList);
expect(menu.classList).not.toContain('active');
});
@@ -193,12 +195,13 @@ describe('SearchPageComponent', () => {
beforeEach(() => {
menu = fixture.debugElement.query(By.css('#search-sidebar-xs')).nativeElement;
comp.isSidebarCollapsed = () => Observable.of(false);
fixture.detectChanges();
sidebarService.expand();
});
it('should open the menu', () => {
sidebarService.isCollapsed.subscribe((a) => {console.log(a)})
sidebarService.isCollapsed().subscribe((v) => console.log('after opening, is collapsed is...: ', v));
console.log(menu.classList);
debugger;
expect(menu.classList).toContain('active');
});

View File

@@ -107,6 +107,6 @@ export class SearchPageComponent implements OnInit, OnDestroy {
}
public isSidebarCollapsed(): Observable<boolean> {
return this.sidebarService.isCollapsed;
return this.sidebarService.isCollapsed();
}
}

View File

@@ -19,7 +19,8 @@ export class SearchSidebarService {
this.isCollapsdeInStored = this.store.select(sidebarCollapsedSelector);
}
get isCollapsed(): Observable<boolean> {
isCollapsed(): Observable<boolean> {
console.log('NEEN');
return Observable.combineLatest(
this.isMobileView,
this.isCollapsdeInStored,

View File

@@ -46,7 +46,7 @@ export function getMetaReducers(config: GlobalConfig): Array<MetaReducer<AppStat
const DEV_MODULES: any[] = [];
if (!ENV_CONFIG.production) {
DEV_MODULES.push(StoreDevtoolsModule.instrument({ maxAge: 50 }));
DEV_MODULES.push(StoreDevtoolsModule.instrument({ maxAge: 500 }));
}
@NgModule({

View File

@@ -0,0 +1,51 @@
import { RouteService } from './route.service';
import { async, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Params } from '@angular/router';
import { Observable } from 'rxjs/Observable';
describe('RouteService', () => {
let service: RouteService;
const paramName1 = 'name';
const paramValue1 = 'Test Name';
const paramName2 = 'id';
const paramValue2a = 'Test id';
const paramValue2b = 'another id';
const nonExistingParamName = 'non existing name';
const nonExistingParamValue = 'non existing value';
const paramObject: Params = {};
paramObject[paramName1] = paramValue1;
paramObject[paramName2] = [paramValue2a, paramValue2b];
beforeEach(async(() => {
return TestBed.configureTestingModule({
declarations: [RouteService],
providers: [
{
provide: ActivatedRoute,
useValue: {
params: Observable.of([paramObject]),
},
},
]
});
}));
beforeEach(() => {
service = new RouteService(TestBed.get(ActivatedRoute));
});
describe('hasQueryParam', () => {
it(' should return true when the parameter name exists', () => {
service.hasQueryParam(paramName1).subscribe((status) => {
expect(status).toBeTruthy();
});
});
it(' should return false when the parameter name does not exists', () => {
service.hasQueryParam(nonExistingParamName).subscribe((status) => {
expect(status).toBeFalsy();
});
});
});
});

View File

@@ -0,0 +1,47 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { ActivatedRoute, convertToParamMap, Params, } from '@angular/router';
import { isNotEmpty } from './empty.util';
@Injectable()
export class RouteService {
constructor(private route: ActivatedRoute) {
}
hasQueryParam(paramName: string): Observable<boolean> {
return this.route.queryParamMap.map((map) => map.has(paramName));
}
hasQueryParamWithValue(paramName: string, paramValue: string): Observable<boolean> {
return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(paramValue) > -1);
}
addQueryParameterValue(paramName: string, paramValue: string): Observable<Params> {
return this.route.queryParams.map((currentParams) => {
const newParam = {};
newParam[paramName] = [...convertToParamMap(currentParams).getAll(paramName), paramValue];
return Object.assign({}, currentParams, newParam);
});
}
removeQueryParameterValue(paramName: string, paramValue: string): Observable<Params> {
return this.route.queryParams.map((currentParams) => {
const newParam = {};
const currentFilterParams = convertToParamMap(currentParams).getAll(paramName);
if (isNotEmpty(currentFilterParams)) {
newParam[paramName] = currentFilterParams.filter((param) => (param !== paramValue));
}
return Object.assign({}, currentParams, newParam);
});
}
removeQueryParameter(paramName: string): Observable<Params> {
return this.route.queryParams.map((currentParams) => {
const newParam = {};
newParam[paramName] = {};
return Object.assign({}, currentParams, newParam);
});
}
}