turned filterValues in to a RemoteData object, added fade animations

This commit is contained in:
Art Lowel
2018-07-24 16:55:11 +02:00
parent 367e832e62
commit 1906f07be3
10 changed files with 104 additions and 53 deletions

View File

@@ -6,8 +6,9 @@
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
<span class="filter-value pl-1">{{value}}</span>
</a>
<ng-container *ngFor="let page of (filterValues$ | async)">
<ng-container *ngFor="let value of (page | async)?.payload.page; let i=index">
<ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<div [@facetLoad]="animationState">
<ng-container *ngFor="let value of page.page; let i=index">
<a *ngIf="!(selectedValues | async).includes(value.value)" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="getAddParams(value.value) | async" queryParamsHandling="merge">
@@ -18,6 +19,7 @@
</span>
</a>
</ng-container>
</div>
</ng-container>
<div class="clearfix toggle-more-filters">
<a class="float-left" *ngIf="!(isLastPage$ | async)"

View File

@@ -2,7 +2,10 @@ import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { FilterType } from '../../../search-service/filter-type.model';
import { renderFacetFor } from '../search-filter-type-decorator';
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
import {
facetLoad,
SearchFacetFilterComponent
} from '../search-facet-filter/search-facet-filter.component';
/**
* This component renders a simple item page.
@@ -14,6 +17,7 @@ import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-
selector: 'ds-search-boolean-filter',
styleUrls: ['./search-boolean-filter.component.scss'],
templateUrl: './search-boolean-filter.component.html',
animations: [facetLoad]
})
@renderFacetFor(FilterType.boolean)

View File

@@ -1,17 +1,20 @@
import { Component, Inject, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { RemoteData } from '../../../../core/data/remote-data';
import { hasNoValue, hasValue, isNotEmpty } from '../../../../shared/empty.util';
import { EmphasizePipe } from '../../../../shared/utils/emphasize.pipe';
import { SearchOptions } from '../../../search-options.model';
import { FacetValue } from '../../../search-service/facet-value.model';
import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
import { hasNoValue, hasValue, isNotEmpty } from '../../../../shared/empty.util';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { SearchService } from '../../../search-service/search.service';
import { SearchOptions } from '../../../search-options.model';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Subscription } from 'rxjs/Subscription';
import { EmphasizePipe } from '../../../../shared/utils/emphasize.pipe';
import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
/**
* This component renders a simple item page.
@@ -26,48 +29,66 @@ import { EmphasizePipe } from '../../../../shared/utils/emphasize.pipe';
export class SearchFacetFilterComponent implements OnInit, OnDestroy {
filterValues: Array<Observable<RemoteData<PaginatedList<FacetValue>>>> = [];
filterValues$: BehaviorSubject<any> = new BehaviorSubject(this.filterValues);
filterValues$: Subject<RemoteData<Array<PaginatedList<FacetValue>>>>;
currentPage: Observable<number>;
isLastPage$: BehaviorSubject<boolean> = new BehaviorSubject(false);
filter: string;
pageChange = false;
sub: Subscription;
private subs: Subscription[] = [];
filterSearchResults: Observable<any[]> = Observable.of([]);
selectedValues: Observable<string[]>;
private collapseNextUpdate = true;
animationState = 'loading';
constructor(protected searchService: SearchService,
protected filterService: SearchFilterService,
protected rdbs: RemoteDataBuildService,
protected router: Router,
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig) {
}
ngOnInit(): void {
this.filterValues$ = new BehaviorSubject(new RemoteData(true, false, undefined, undefined, undefined));
this.currentPage = this.getCurrentPage().distinctUntilChanged();
this.selectedValues = this.filterService.getSelectedValuesForFilter(this.filterConfig);
const searchOptions = this.filterService.getSearchOptions().distinctUntilChanged();
searchOptions.subscribe((options) => this.updateFilterValueList(options));
this.subs.push(searchOptions.subscribe((options) => this.updateFilterValueList(options)));
const facetValues = Observable.combineLatest(searchOptions, this.currentPage, (options, page) => {
return {values: this.searchService.getFacetValuesFor(this.filterConfig, page, options), page: page};
});
facetValues.subscribe((facetOutcome) => {
this.subs.push(facetValues.subscribe((facetOutcome) => {
const newValues$ = facetOutcome.values;
if (facetOutcome.page > 1) {
this.filterValues = [...this.filterValues, newValues$];
} else {
this.filterValues = [newValues$]
if (this.collapseNextUpdate) {
this.showFirstPageOnly();
facetOutcome.page = 1;
this.collapseNextUpdate = false;
}
if (facetOutcome.page === 1) {
this.filterValues = [];
}
this.filterValues$.next(this.filterValues);
newValues$.first().subscribe((rd) => {
this.filterValues = [...this.filterValues, newValues$];
this.subs.push(this.rdbs.aggregate(this.filterValues).subscribe((rd: RemoteData<Array<PaginatedList<FacetValue>>>) => {
this.animationState = 'ready';
this.filterValues$.next(rd);
}));
this.subs.push(newValues$.first().subscribe((rd) => {
this.isLastPage$.next(hasNoValue(rd.payload.next))
});
});
}));
}));
}
updateFilterValueList(options: SearchOptions) {
// this.unsubscribe();
this.showFirstPageOnly();
// this.showFirstPageOnly();
this.animationState = 'loading';
this.collapseNextUpdate = true;
this.filter = '';
}
@@ -138,13 +159,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
}
ngOnDestroy(): void {
this.unsubscribe();
}
unsubscribe(): void {
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
this.subs
.filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe());
}
findSuggestions(data): void {
@@ -171,3 +188,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
return new EmphasizePipe().transform(facet.value, query) + ' (' + facet.count + ')';
}
}
export const facetLoad = trigger('facetLoad', [
state('ready', style({ opacity: 1 })),
state('loading', style({ opacity: 0 })),
transition('loading <=> ready', animate(100)),
]);

View File

@@ -6,8 +6,9 @@
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
<span class="filter-value pl-1">{{value}}</span>
</a>
<ng-container *ngFor="let page of (filterValues$ | async)">
<ng-container *ngFor="let value of (page | async)?.payload.page; let i=index">
<ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<div [@facetLoad]="animationState">
<ng-container *ngFor="let value of page.page; let i=index">
<a *ngIf="!(selectedValues | async).includes(value.value)" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="getAddParams(value.value) | async" queryParamsHandling="merge" >
@@ -18,6 +19,7 @@
</span>
</a>
</ng-container>
</div>
</ng-container>
<div class="clearfix toggle-more-filters">
<a class="float-left" *ngIf="!(isLastPage$ | async)"

View File

@@ -3,7 +3,10 @@ import { FacetValue } from '../../../search-service/facet-value.model';
import { Observable } from 'rxjs/Observable';
import { FilterType } from '../../../search-service/filter-type.model';
import { renderFacetFor } from '../search-filter-type-decorator';
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
import {
facetLoad,
SearchFacetFilterComponent
} from '../search-facet-filter/search-facet-filter.component';
/**
* This component renders a simple item page.
@@ -15,6 +18,7 @@ import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-
selector: 'ds-search-hierarchy-filter',
styleUrls: ['./search-hierarchy-filter.component.scss'],
templateUrl: './search-hierarchy-filter.component.html',
animations: [facetLoad]
})
@renderFacetFor(FilterType.hierarchy)

View File

@@ -23,8 +23,9 @@
ngDefaultControl></nouislider>
</ng-container>
<ng-container *ngFor="let page of (filterValues$ | async)">
<ng-container *ngFor="let value of (page | async)?.payload.page; let i=index">
<ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<div [@facetLoad]="animationState">
<ng-container *ngFor="let value of page.page; let i=index">
<a *ngIf="!(selectedValues | async).includes(value.value)" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="getAddParams(value.value) | async" queryParamsHandling="merge">
@@ -34,6 +35,7 @@
</span>
</a>
</ng-container>
</div>
</ng-container>
</div>
</div>

View File

@@ -1,8 +1,12 @@
import { isPlatformBrowser } from '@angular/common';
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { FilterType } from '../../../search-service/filter-type.model';
import { renderFacetFor } from '../search-filter-type-decorator';
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
import {
facetLoad,
SearchFacetFilterComponent
} from '../search-facet-filter/search-facet-filter.component';
import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
import { SearchService } from '../../../search-service/search.service';
@@ -24,6 +28,7 @@ const rangeDelimiter = '-';
selector: 'ds-search-range-filter',
styleUrls: ['./search-range-filter.component.scss'],
templateUrl: './search-range-filter.component.html',
animations: [facetLoad]
})
@renderFacetFor(FilterType.range)
@@ -35,10 +40,11 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
constructor(protected searchService: SearchService,
protected filterService: SearchFilterService,
protected router: Router,
protected rdbs: RemoteDataBuildService,
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
@Inject(PLATFORM_ID) private platformId: any,
private route: ActivatedRoute) {
super(searchService, filterService, router, filterConfig);
super(searchService, filterService, rdbs, router, filterConfig);
}
ngOnInit(): void {

View File

@@ -6,18 +6,22 @@
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
<span class="filter-value pl-1">{{value}}</span>
</a>
<ng-container *ngFor="let page of (filterValues$ | async)">
<ng-container *ngFor="let value of (page | async)?.payload.page; let i=index">
<ng-container *ngVar="(filterValues$ | async) as filterValuesRD">
<div [@facetLoad]="animationState">
<ng-container *ngFor="let page of filterValuesRD?.payload">
<ng-container *ngFor="let value of page.page; let i=index">
<a *ngIf="!(selectedValues | async).includes(value.value)" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="getAddParams(value.value) | async" queryParamsHandling="merge" >
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{value.value}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{value.count}}</span>
</span>
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{value.value}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{value.count}}</span>
</span>
</a>
</ng-container>
</ng-container>
</div>
</ng-container>
<div class="clearfix toggle-more-filters">
<a class="float-left" *ngIf="!(isLastPage$ | async)"

View File

@@ -1,9 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { FacetValue } from '../../../search-service/facet-value.model';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { Component, HostBinding, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { FilterType } from '../../../search-service/filter-type.model';
import {
facetLoad,
SearchFacetFilterComponent
} from '../search-facet-filter/search-facet-filter.component';
import { renderFacetFor } from '../search-filter-type-decorator';
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
/**
* This component renders a simple item page.
@@ -15,6 +18,7 @@ import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-
selector: 'ds-search-text-filter',
styleUrls: ['./search-text-filter.component.scss'],
templateUrl: './search-text-filter.component.html',
animations: [facetLoad]
})
@renderFacetFor(FilterType.text)

View File

@@ -1,5 +1,5 @@
<div>
<label>{{ message }}</label>
<label *ngIf="message">{{ message }}</label>
<div class="loader">
<span class="l-1"></span>
<span class="l-2"></span>