mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
[CST-6494] Update filter values when a workflow action is dispatched
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||||
import { Injectable, InjectionToken } from '@angular/core';
|
import { Injectable, InjectionToken } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
@@ -26,6 +26,7 @@ const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
|
|||||||
|
|
||||||
export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionToken<SearchFilterConfig>('filterConfig');
|
export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionToken<SearchFilterConfig>('filterConfig');
|
||||||
export const IN_PLACE_SEARCH: InjectionToken<boolean> = new InjectionToken<boolean>('inPlaceSearch');
|
export const IN_PLACE_SEARCH: InjectionToken<boolean> = new InjectionToken<boolean>('inPlaceSearch');
|
||||||
|
export const REFRESH_FILTER: InjectionToken<BehaviorSubject<any>> = new InjectionToken<boolean>('refreshFilters');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all actions that have to do with search filters and facets
|
* Service that performs all actions that have to do with search filters and facets
|
||||||
|
@@ -262,9 +262,11 @@ export class SearchService implements OnDestroy {
|
|||||||
* @param {number} valuePage The page number of the filter values
|
* @param {number} valuePage The page number of the filter values
|
||||||
* @param {SearchOptions} searchOptions The search configuration for the current search
|
* @param {SearchOptions} searchOptions The search configuration for the current search
|
||||||
* @param {string} filterQuery The optional query used to filter out filter values
|
* @param {string} filterQuery The optional query used to filter out filter values
|
||||||
|
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||||
|
* no valid cached version. Defaults to true
|
||||||
* @returns {Observable<RemoteData<PaginatedList<FacetValue>>>} Emits the given page of facet values
|
* @returns {Observable<RemoteData<PaginatedList<FacetValue>>>} Emits the given page of facet values
|
||||||
*/
|
*/
|
||||||
getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable<RemoteData<FacetValues>> {
|
getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string, useCachedVersionIfAvailable = true): Observable<RemoteData<FacetValues>> {
|
||||||
let href;
|
let href;
|
||||||
const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`];
|
const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`];
|
||||||
if (hasValue(filterQuery)) {
|
if (hasValue(filterQuery)) {
|
||||||
@@ -282,7 +284,7 @@ export class SearchService implements OnDestroy {
|
|||||||
return FacetValueResponseParsingService;
|
return FacetValueResponseParsingService;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.requestService.send(request, true);
|
this.requestService.send(request, useCachedVersionIfAvailable);
|
||||||
|
|
||||||
return this.rdb.buildFromHref(href);
|
return this.rdb.buildFromHref(href);
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
[importable]="importable"
|
[importable]="importable"
|
||||||
[importConfig]="importConfig"
|
[importConfig]="importConfig"
|
||||||
(importObject)="importObject.emit($event)"
|
(importObject)="importObject.emit($event)"
|
||||||
(contentChange)="contentChange.emit()"
|
(contentChange)="contentChange.emit($event)"
|
||||||
(prev)="goPrev()"
|
(prev)="goPrev()"
|
||||||
(next)="goNext()"
|
(next)="goNext()"
|
||||||
*ngIf="(currentMode$ | async) === viewModeEnum.ListElement">
|
*ngIf="(currentMode$ | async) === viewModeEnum.ListElement">
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
[context]="context"
|
[context]="context"
|
||||||
[hidePaginationDetail]="hidePaginationDetail"
|
[hidePaginationDetail]="hidePaginationDetail"
|
||||||
[showPaginator]="showPaginator"
|
[showPaginator]="showPaginator"
|
||||||
|
(contentChange)="contentChange.emit($event)"
|
||||||
*ngIf="(currentMode$ | async) === viewModeEnum.DetailedListElement">
|
*ngIf="(currentMode$ | async) === viewModeEnum.DetailedListElement">
|
||||||
</ds-object-detail>
|
</ds-object-detail>
|
||||||
|
|
||||||
|
@@ -50,6 +50,11 @@ export class ObjectCollectionComponent implements OnInit {
|
|||||||
@Input() hideGear = false;
|
@Input() hideGear = false;
|
||||||
@Input() selectable = false;
|
@Input() selectable = false;
|
||||||
@Input() selectionConfig: {repeatable: boolean, listId: string};
|
@Input() selectionConfig: {repeatable: boolean, listId: string};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit custom event for listable object custom actions.
|
||||||
|
*/
|
||||||
|
@Output() customEvent = new EventEmitter<any>();
|
||||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
|
@@ -5,7 +5,9 @@ import { ListableObject } from '../listable-object.model';
|
|||||||
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
|
||||||
import { Context } from '../../../../core/shared/context.model';
|
import { Context } from '../../../../core/shared/context.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { ItemListElementComponent } from '../../../object-list/item-list-element/item-types/item/item-list-element.component';
|
import {
|
||||||
|
ItemListElementComponent
|
||||||
|
} from '../../../object-list/item-list-element/item-types/item/item-list-element.component';
|
||||||
import { ListableObjectDirective } from './listable-object.directive';
|
import { ListableObjectDirective } from './listable-object.directive';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
@@ -146,7 +148,7 @@ describe('ListableObjectComponentLoaderComponent', () => {
|
|||||||
expect((comp as any).instantiateComponent).not.toHaveBeenCalled();
|
expect((comp as any).instantiateComponent).not.toHaveBeenCalled();
|
||||||
|
|
||||||
(listableComponent as any).reloadedObject.emit(reloadedObject);
|
(listableComponent as any).reloadedObject.emit(reloadedObject);
|
||||||
tick();
|
tick(200);
|
||||||
|
|
||||||
expect((comp as any).instantiateComponent).toHaveBeenCalledWith(reloadedObject);
|
expect((comp as any).instantiateComponent).toHaveBeenCalledWith(reloadedObject);
|
||||||
}));
|
}));
|
||||||
@@ -155,7 +157,7 @@ describe('ListableObjectComponentLoaderComponent', () => {
|
|||||||
expect((comp as any).contentChange.emit).not.toHaveBeenCalled();
|
expect((comp as any).contentChange.emit).not.toHaveBeenCalled();
|
||||||
|
|
||||||
(listableComponent as any).reloadedObject.emit(reloadedObject);
|
(listableComponent as any).reloadedObject.emit(reloadedObject);
|
||||||
tick();
|
tick(200);
|
||||||
|
|
||||||
expect((comp as any).contentChange.emit).toHaveBeenCalledWith(reloadedObject);
|
expect((comp as any).contentChange.emit).toHaveBeenCalledWith(reloadedObject);
|
||||||
}));
|
}));
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
ComponentFactoryResolver,
|
ComponentFactoryResolver,
|
||||||
|
ComponentRef,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
Input,
|
Input,
|
||||||
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
ViewChild,
|
|
||||||
EventEmitter,
|
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
OnChanges,
|
ViewChild
|
||||||
ComponentRef
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ListableObject } from '../listable-object.model';
|
import { ListableObject } from '../listable-object.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
@@ -187,7 +187,10 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges
|
|||||||
this.compRef.destroy();
|
this.compRef.destroy();
|
||||||
this.object = reloadedObject;
|
this.object = reloadedObject;
|
||||||
this.instantiateComponent(reloadedObject);
|
this.instantiateComponent(reloadedObject);
|
||||||
|
// Add delay before emitting event to allow the new object is instantiated
|
||||||
|
setTimeout(() => {
|
||||||
this.contentChange.emit(reloadedObject);
|
this.contentChange.emit(reloadedObject);
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -14,12 +14,14 @@
|
|||||||
(sortFieldChange)="onSortFieldChange($event)"
|
(sortFieldChange)="onSortFieldChange($event)"
|
||||||
(paginationChange)="onPaginationChange($event)"
|
(paginationChange)="onPaginationChange($event)"
|
||||||
(prev)="goPrev()"
|
(prev)="goPrev()"
|
||||||
(next)="goNext()"
|
(next)="goNext()">
|
||||||
>
|
|
||||||
<div class="row mt-2" *ngIf="objects?.hasSucceeded" @fadeIn>
|
<div class="row mt-2" *ngIf="objects?.hasSucceeded" @fadeIn>
|
||||||
<div class="col"
|
<div class="col"
|
||||||
*ngFor="let object of objects?.payload?.page">
|
*ngFor="let object of objects?.payload?.page">
|
||||||
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [context]="context"></ds-listable-object-component-loader>
|
<ds-listable-object-component-loader [object]="object"
|
||||||
|
[viewMode]="viewMode"
|
||||||
|
[context]="context"
|
||||||
|
(contentChange)="contentChange.emit($event)"></ds-listable-object-component-loader>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ds-error *ngIf="objects.hasFailed" message="{{'error.objects' | translate}}"></ds-error>
|
<ds-error *ngIf="objects.hasFailed" message="{{'error.objects' | translate}}"></ds-error>
|
||||||
|
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||||
ChangeDetectionStrategy,
|
|
||||||
Component,
|
|
||||||
EventEmitter,
|
|
||||||
Input,
|
|
||||||
Output,
|
|
||||||
ViewEncapsulation
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
@@ -71,6 +64,11 @@ export class ObjectDetailComponent {
|
|||||||
*/
|
*/
|
||||||
@Input() showPaginator = true;
|
@Input() showPaginator = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when one of the listed object has changed.
|
||||||
|
*/
|
||||||
|
@Output() contentChange = new EventEmitter<any>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If showPaginator is set to true, emit when the previous button is clicked
|
* If showPaginator is set to true, emit when the previous button is clicked
|
||||||
*/
|
*/
|
||||||
|
@@ -14,8 +14,7 @@
|
|||||||
(sortFieldChange)="onSortFieldChange($event)"
|
(sortFieldChange)="onSortFieldChange($event)"
|
||||||
(paginationChange)="onPaginationChange($event)"
|
(paginationChange)="onPaginationChange($event)"
|
||||||
(prev)="goPrev()"
|
(prev)="goPrev()"
|
||||||
(next)="goNext()"
|
(next)="goNext()">
|
||||||
>
|
|
||||||
<ul *ngIf="objects?.hasSucceeded" class="list-unstyled" [ngClass]="{'ml-4': selectable}">
|
<ul *ngIf="objects?.hasSucceeded" class="list-unstyled" [ngClass]="{'ml-4': selectable}">
|
||||||
<li *ngFor="let object of objects?.payload?.page; let i = index; let last = last" class="mt-4 mb-4 d-flex" [class.border-bottom]="hasBorder && !last" [attr.data-test]="'list-object' | dsBrowserOnly">
|
<li *ngFor="let object of objects?.payload?.page; let i = index; let last = last" class="mt-4 mb-4 d-flex" [class.border-bottom]="hasBorder && !last" [attr.data-test]="'list-object' | dsBrowserOnly">
|
||||||
<ds-selectable-list-item-control *ngIf="selectable" [index]="i"
|
<ds-selectable-list-item-control *ngIf="selectable" [index]="i"
|
||||||
@@ -28,8 +27,7 @@
|
|||||||
(importObject)="importObject.emit($event)"></ds-importable-list-item-control>
|
(importObject)="importObject.emit($event)"></ds-importable-list-item-control>
|
||||||
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType"
|
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType"
|
||||||
[listID]="selectionConfig?.listId"
|
[listID]="selectionConfig?.listId"
|
||||||
(contentChange)="contentChange.emit()"
|
(contentChange)="contentChange.emit($event)"></ds-listable-object-component-loader>
|
||||||
></ds-listable-object-component-loader>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ds-pagination>
|
</ds-pagination>
|
||||||
|
@@ -74,7 +74,7 @@ export class ObjectListComponent {
|
|||||||
/**
|
/**
|
||||||
* Config used for the import button
|
* Config used for the import button
|
||||||
*/
|
*/
|
||||||
@Input() importConfig: { importLabel: string };
|
@Input() importConfig: { buttonLabel: string };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination
|
* Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination
|
||||||
@@ -221,4 +221,5 @@ export class ObjectListComponent {
|
|||||||
goNext() {
|
goNext() {
|
||||||
this.next.emit(true);
|
this.next.emit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,14 @@ import { Component, Injector, Input, OnInit } from '@angular/core';
|
|||||||
import { renderFilterType } from '../search-filter-type-decorator';
|
import { renderFilterType } from '../search-filter-type-decorator';
|
||||||
import { FilterType } from '../../../models/filter-type.model';
|
import { FilterType } from '../../../models/filter-type.model';
|
||||||
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
||||||
import { FILTER_CONFIG, IN_PLACE_SEARCH } from '../../../../../core/shared/search/search-filter.service';
|
import {
|
||||||
|
FILTER_CONFIG,
|
||||||
|
IN_PLACE_SEARCH,
|
||||||
|
REFRESH_FILTER
|
||||||
|
} from '../../../../../core/shared/search/search-filter.service';
|
||||||
import { GenericConstructor } from '../../../../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../../../../core/shared/generic-constructor';
|
||||||
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
|
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-facet-filter-wrapper',
|
selector: 'ds-search-facet-filter-wrapper',
|
||||||
@@ -25,6 +30,11 @@ export class SearchFacetFilterWrapperComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() inPlaceSearch;
|
@Input() inPlaceSearch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits when the search filters values may be stale, and so they must be refreshed.
|
||||||
|
*/
|
||||||
|
@Input() refreshFilters: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor of the search facet filter that should be rendered, based on the filter config's type
|
* The constructor of the search facet filter that should be rendered, based on the filter config's type
|
||||||
*/
|
*/
|
||||||
@@ -45,7 +55,8 @@ export class SearchFacetFilterWrapperComponent implements OnInit {
|
|||||||
this.objectInjector = Injector.create({
|
this.objectInjector = Injector.create({
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: FILTER_CONFIG, useFactory: () => (this.filterConfig), deps: [] },
|
{ provide: FILTER_CONFIG, useFactory: () => (this.filterConfig), deps: [] },
|
||||||
{ provide: IN_PLACE_SEARCH, useFactory: () => (this.inPlaceSearch), deps: [] }
|
{ provide: IN_PLACE_SEARCH, useFactory: () => (this.inPlaceSearch), deps: [] },
|
||||||
|
{ provide: REFRESH_FILTER, useFactory: () => (this.refreshFilters), deps: [] }
|
||||||
],
|
],
|
||||||
parent: this.injector
|
parent: this.injector
|
||||||
});
|
});
|
||||||
|
@@ -5,13 +5,14 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
import {
|
import {
|
||||||
FILTER_CONFIG,
|
FILTER_CONFIG,
|
||||||
IN_PLACE_SEARCH,
|
IN_PLACE_SEARCH,
|
||||||
|
REFRESH_FILTER,
|
||||||
SearchFilterService
|
SearchFilterService
|
||||||
} from '../../../../../core/shared/search/search-filter.service';
|
} from '../../../../../core/shared/search/search-filter.service';
|
||||||
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
||||||
import { FilterType } from '../../../models/filter-type.model';
|
import { FilterType } from '../../../models/filter-type.model';
|
||||||
import { FacetValue } from '../../../models/facet-value.model';
|
import { FacetValue } from '../../../models/facet-value.model';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { BehaviorSubject, of as observableOf } from 'rxjs';
|
||||||
import { SearchService } from '../../../../../core/shared/search/search.service';
|
import { SearchService } from '../../../../../core/shared/search/search.service';
|
||||||
import { SearchServiceStub } from '../../../../testing/search-service.stub';
|
import { SearchServiceStub } from '../../../../testing/search-service.stub';
|
||||||
import { buildPaginatedList } from '../../../../../core/data/paginated-list.model';
|
import { buildPaginatedList } from '../../../../../core/data/paginated-list.model';
|
||||||
@@ -97,6 +98,7 @@ describe('SearchFacetFilterComponent', () => {
|
|||||||
{ provide: RemoteDataBuildService, useValue: { aggregate: () => observableOf({}) } },
|
{ provide: RemoteDataBuildService, useValue: { aggregate: () => observableOf({}) } },
|
||||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
||||||
{ provide: IN_PLACE_SEARCH, useValue: false },
|
{ provide: IN_PLACE_SEARCH, useValue: false },
|
||||||
|
{ provide: REFRESH_FILTER, useValue: new BehaviorSubject<boolean>(false) },
|
||||||
{
|
{
|
||||||
provide: SearchFilterService, useValue: {
|
provide: SearchFilterService, useValue: {
|
||||||
getSelectedValuesForFilter: () => observableOf(selectedValues),
|
getSelectedValuesForFilter: () => observableOf(selectedValues),
|
||||||
|
@@ -6,7 +6,7 @@ import {
|
|||||||
Subject,
|
Subject,
|
||||||
Subscription
|
Subscription
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { distinctUntilChanged, map, switchMap, take, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
@@ -21,6 +21,7 @@ import { SearchService } from '../../../../../core/shared/search/search.service'
|
|||||||
import {
|
import {
|
||||||
FILTER_CONFIG,
|
FILTER_CONFIG,
|
||||||
IN_PLACE_SEARCH,
|
IN_PLACE_SEARCH,
|
||||||
|
REFRESH_FILTER,
|
||||||
SearchFilterService
|
SearchFilterService
|
||||||
} from '../../../../../core/shared/search/search-filter.service';
|
} from '../../../../../core/shared/search/search-filter.service';
|
||||||
import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service';
|
import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service';
|
||||||
@@ -98,7 +99,8 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
|
|||||||
protected router: Router,
|
protected router: Router,
|
||||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||||
@Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
|
@Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
|
||||||
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig) {
|
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
|
||||||
|
@Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject<boolean>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,66 +112,15 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
|
|||||||
this.currentPage = this.getCurrentPage().pipe(distinctUntilChanged());
|
this.currentPage = this.getCurrentPage().pipe(distinctUntilChanged());
|
||||||
|
|
||||||
this.searchOptions$ = this.searchConfigService.searchOptions;
|
this.searchOptions$ = this.searchConfigService.searchOptions;
|
||||||
this.subs.push(this.searchOptions$.subscribe(() => this.updateFilterValueList()));
|
this.subs.push(
|
||||||
const facetValues$ = observableCombineLatest(this.searchOptions$, this.currentPage).pipe(
|
this.searchOptions$.subscribe(() => this.updateFilterValueList()),
|
||||||
map(([options, page]) => {
|
this.refreshFilters.asObservable().pipe(
|
||||||
return { options, page };
|
filter((toRefresh: boolean) => toRefresh),
|
||||||
}),
|
).subscribe(() => {
|
||||||
switchMap(({ options, page }) => {
|
this.retrieveFilterValues(false);
|
||||||
return this.searchService.getFacetValuesFor(this.filterConfig, page, options)
|
|
||||||
.pipe(
|
|
||||||
getFirstSucceededRemoteData(),
|
|
||||||
map((results) => {
|
|
||||||
return {
|
|
||||||
values: observableOf(results),
|
|
||||||
page: page
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
this.retrieveFilterValues();
|
||||||
let filterValues = [];
|
|
||||||
this.subs.push(facetValues$.subscribe((facetOutcome) => {
|
|
||||||
const newValues$ = facetOutcome.values;
|
|
||||||
|
|
||||||
if (this.collapseNextUpdate) {
|
|
||||||
this.showFirstPageOnly();
|
|
||||||
facetOutcome.page = 1;
|
|
||||||
this.collapseNextUpdate = false;
|
|
||||||
}
|
|
||||||
if (facetOutcome.page === 1) {
|
|
||||||
filterValues = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
filterValues = [...filterValues, newValues$];
|
|
||||||
|
|
||||||
this.subs.push(this.rdbs.aggregate(filterValues).pipe(
|
|
||||||
tap((rd: RemoteData<PaginatedList<FacetValue>[]>) => {
|
|
||||||
this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig).pipe(
|
|
||||||
map((selectedValues) => {
|
|
||||||
return selectedValues.map((value: string) => {
|
|
||||||
const fValue = [].concat(...rd.payload.map((page) => page.page)).find((facetValue: FacetValue) => this.getFacetValue(facetValue) === value);
|
|
||||||
if (hasValue(fValue)) {
|
|
||||||
return fValue;
|
|
||||||
}
|
|
||||||
const filterValue = stripOperatorFromFilterValue(value);
|
|
||||||
return Object.assign(new FacetValue(), { label: filterValue, value: filterValue });
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
).subscribe((rd: RemoteData<PaginatedList<FacetValue>[]>) => {
|
|
||||||
this.animationState = 'ready';
|
|
||||||
this.filterValues$.next(rd);
|
|
||||||
|
|
||||||
}));
|
|
||||||
this.subs.push(newValues$.pipe(take(1)).subscribe((rd) => {
|
|
||||||
this.isLastPage$.next(hasNoValue(rd.payload.next));
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -324,6 +275,67 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
|
|||||||
return getFacetValueForType(facet, this.filterConfig);
|
return getFacetValueForType(facet, this.filterConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected retrieveFilterValues(useCachedVersionIfAvailable = true) {
|
||||||
|
const facetValues$ = observableCombineLatest([this.searchOptions$, this.currentPage]).pipe(
|
||||||
|
map(([options, page]) => {
|
||||||
|
return { options, page };
|
||||||
|
}),
|
||||||
|
switchMap(({ options, page }) => {
|
||||||
|
return this.searchService.getFacetValuesFor(this.filterConfig, page, options, null, useCachedVersionIfAvailable)
|
||||||
|
.pipe(
|
||||||
|
getFirstSucceededRemoteData(),
|
||||||
|
map((results) => {
|
||||||
|
return {
|
||||||
|
values: observableOf(results),
|
||||||
|
page: page
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let filterValues = [];
|
||||||
|
this.subs.push(facetValues$.subscribe((facetOutcome) => {
|
||||||
|
const newValues$ = facetOutcome.values;
|
||||||
|
|
||||||
|
if (this.collapseNextUpdate) {
|
||||||
|
this.showFirstPageOnly();
|
||||||
|
facetOutcome.page = 1;
|
||||||
|
this.collapseNextUpdate = false;
|
||||||
|
}
|
||||||
|
if (facetOutcome.page === 1) {
|
||||||
|
filterValues = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
filterValues = [...filterValues, newValues$];
|
||||||
|
|
||||||
|
this.subs.push(this.rdbs.aggregate(filterValues).pipe(
|
||||||
|
tap((rd: RemoteData<PaginatedList<FacetValue>[]>) => {
|
||||||
|
this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig).pipe(
|
||||||
|
map((selectedValues) => {
|
||||||
|
return selectedValues.map((value: string) => {
|
||||||
|
const fValue = [].concat(...rd.payload.map((page) => page.page)).find((facetValue: FacetValue) => this.getFacetValue(facetValue) === value);
|
||||||
|
if (hasValue(fValue)) {
|
||||||
|
return fValue;
|
||||||
|
}
|
||||||
|
const filterValue = stripOperatorFromFilterValue(value);
|
||||||
|
return Object.assign(new FacetValue(), { label: filterValue, value: filterValue });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
).subscribe((rd: RemoteData<PaginatedList<FacetValue>[]>) => {
|
||||||
|
this.animationState = 'ready';
|
||||||
|
this.filterValues$.next(rd);
|
||||||
|
|
||||||
|
}));
|
||||||
|
this.subs.push(newValues$.pipe(take(1)).subscribe((rd) => {
|
||||||
|
this.isLastPage$.next(hasNoValue(rd.payload.next));
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the facet value string, so if the query matches part of the value, it's emphasized in the value
|
* Transforms the facet value string, so if the query matches part of the value, it's emphasized in the value
|
||||||
* @param {FacetValue} facet The value of the facet as returned by the server
|
* @param {FacetValue} facet The value of the facet as returned by the server
|
||||||
|
@@ -19,7 +19,8 @@
|
|||||||
class="search-filter-wrapper" [ngClass]="{ 'closed' : closed, 'notab': notab }">
|
class="search-filter-wrapper" [ngClass]="{ 'closed' : closed, 'notab': notab }">
|
||||||
<ds-search-facet-filter-wrapper
|
<ds-search-facet-filter-wrapper
|
||||||
[filterConfig]="filter"
|
[filterConfig]="filter"
|
||||||
[inPlaceSearch]="inPlaceSearch">
|
[inPlaceSearch]="inPlaceSearch"
|
||||||
|
[refreshFilters]="refreshFilters" >
|
||||||
</ds-search-facet-filter-wrapper>
|
</ds-search-facet-filter-wrapper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
import { Component, Inject, Input, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||||
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
|
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SearchFilterConfig } from '../../models/search-filter-config.model';
|
import { SearchFilterConfig } from '../../models/search-filter-config.model';
|
||||||
@@ -33,6 +33,11 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() inPlaceSearch;
|
@Input() inPlaceSearch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits when the search filters values may be stale, and so they must be refreshed.
|
||||||
|
*/
|
||||||
|
@Input() refreshFilters: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when the filter is 100% collapsed in the UI
|
* True when the filter is 100% collapsed in the UI
|
||||||
*/
|
*/
|
||||||
|
@@ -1,17 +1,18 @@
|
|||||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component';
|
import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component';
|
||||||
import { SearchService } from '../../../../../core/shared/search/search.service';
|
import { SearchService } from '../../../../../core/shared/search/search.service';
|
||||||
import {
|
import {
|
||||||
SearchFilterService,
|
|
||||||
FILTER_CONFIG,
|
FILTER_CONFIG,
|
||||||
IN_PLACE_SEARCH
|
IN_PLACE_SEARCH,
|
||||||
|
REFRESH_FILTER,
|
||||||
|
SearchFilterService
|
||||||
} from '../../../../../core/shared/search/search-filter.service';
|
} from '../../../../../core/shared/search/search-filter.service';
|
||||||
import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service';
|
||||||
import { SearchFiltersComponent } from '../../search-filters.component';
|
import { SearchFiltersComponent } from '../../search-filters.component';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { RouterStub } from '../../../../testing/router.stub';
|
import { RouterStub } from '../../../../testing/router.stub';
|
||||||
import { SearchServiceStub } from '../../../../testing/search-service.stub';
|
import { SearchServiceStub } from '../../../../testing/search-service.stub';
|
||||||
import { of as observableOf, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component';
|
||||||
import { SearchConfigurationServiceStub } from '../../../../testing/search-configuration-service.stub';
|
import { SearchConfigurationServiceStub } from '../../../../testing/search-configuration-service.stub';
|
||||||
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
||||||
@@ -21,7 +22,7 @@ import {
|
|||||||
} from '../../../../input-suggestions/filter-suggestions/filter-input-suggestions.component';
|
} from '../../../../input-suggestions/filter-suggestions/filter-input-suggestions.component';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { NO_ERRORS_SCHEMA, ChangeDetectionStrategy } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils';
|
||||||
import { FacetValue } from '../../../models/facet-value.model';
|
import { FacetValue } from '../../../models/facet-value.model';
|
||||||
import { FilterType } from '../../../models/filter-type.model';
|
import { FilterType } from '../../../models/filter-type.model';
|
||||||
@@ -112,7 +113,8 @@ describe('SearchHierarchyFilterComponent', () => {
|
|||||||
{ provide: Router, useValue: new RouterStub() },
|
{ provide: Router, useValue: new RouterStub() },
|
||||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
||||||
{ provide: IN_PLACE_SEARCH, useValue: false },
|
{ provide: IN_PLACE_SEARCH, useValue: false },
|
||||||
{ provide: FILTER_CONFIG, useValue: new SearchFilterConfig() }
|
{ provide: FILTER_CONFIG, useValue: new SearchFilterConfig() },
|
||||||
|
{ provide: REFRESH_FILTER, useValue: new BehaviorSubject<boolean>(false) }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(SearchHierarchyFilterComponent, {
|
}).overrideComponent(SearchHierarchyFilterComponent, {
|
||||||
@@ -140,7 +142,7 @@ describe('SearchHierarchyFilterComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to the correct filter with the query operator', () => {
|
it('should navigate to the correct filter with the query operator', () => {
|
||||||
expect((comp as any).searchService.getFacetValuesFor).toHaveBeenCalledWith(comp.filterConfig, 0, {});
|
expect((comp as any).searchService.getFacetValuesFor).toHaveBeenCalledWith(comp.filterConfig, 0, {}, null, true);
|
||||||
|
|
||||||
const searchQuery = 'MARVEL';
|
const searchQuery = 'MARVEL';
|
||||||
comp.onSubmit(searchQuery);
|
comp.onSubmit(searchQuery);
|
||||||
|
@@ -5,13 +5,14 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
import {
|
import {
|
||||||
FILTER_CONFIG,
|
FILTER_CONFIG,
|
||||||
IN_PLACE_SEARCH,
|
IN_PLACE_SEARCH,
|
||||||
|
REFRESH_FILTER,
|
||||||
SearchFilterService
|
SearchFilterService
|
||||||
} from '../../../../../core/shared/search/search-filter.service';
|
} from '../../../../../core/shared/search/search-filter.service';
|
||||||
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
||||||
import { FilterType } from '../../../models/filter-type.model';
|
import { FilterType } from '../../../models/filter-type.model';
|
||||||
import { FacetValue } from '../../../models/facet-value.model';
|
import { FacetValue } from '../../../models/facet-value.model';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { BehaviorSubject, of as observableOf } from 'rxjs';
|
||||||
import { SearchService } from '../../../../../core/shared/search/search.service';
|
import { SearchService } from '../../../../../core/shared/search/search.service';
|
||||||
import { SearchServiceStub } from '../../../../testing/search-service.stub';
|
import { SearchServiceStub } from '../../../../testing/search-service.stub';
|
||||||
import { buildPaginatedList } from '../../../../../core/data/paginated-list.model';
|
import { buildPaginatedList } from '../../../../../core/data/paginated-list.model';
|
||||||
@@ -104,6 +105,7 @@ describe('SearchRangeFilterComponent', () => {
|
|||||||
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf({}) } },
|
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf({}) } },
|
||||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
||||||
{ provide: IN_PLACE_SEARCH, useValue: false },
|
{ provide: IN_PLACE_SEARCH, useValue: false },
|
||||||
|
{ provide: REFRESH_FILTER, useValue: new BehaviorSubject<boolean>(false) },
|
||||||
{
|
{
|
||||||
provide: SearchFilterService, useValue: {
|
provide: SearchFilterService, useValue: {
|
||||||
getSelectedValuesForFilter: () => selectedValues,
|
getSelectedValuesForFilter: () => selectedValues,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { combineLatest as observableCombineLatest, Subscription } from 'rxjs';
|
import { BehaviorSubject, combineLatest as observableCombineLatest, Subscription } from 'rxjs';
|
||||||
import { map, startWith } from 'rxjs/operators';
|
import { map, startWith } from 'rxjs/operators';
|
||||||
import { isPlatformBrowser } from '@angular/common';
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
|
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
|
||||||
@@ -10,6 +10,7 @@ import { SearchFilterConfig } from '../../../models/search-filter-config.model';
|
|||||||
import {
|
import {
|
||||||
FILTER_CONFIG,
|
FILTER_CONFIG,
|
||||||
IN_PLACE_SEARCH,
|
IN_PLACE_SEARCH,
|
||||||
|
REFRESH_FILTER,
|
||||||
SearchFilterService
|
SearchFilterService
|
||||||
} from '../../../../../core/shared/search/search-filter.service';
|
} from '../../../../../core/shared/search/search-filter.service';
|
||||||
import { SearchService } from '../../../../../core/shared/search/search.service';
|
import { SearchService } from '../../../../../core/shared/search/search.service';
|
||||||
@@ -86,8 +87,9 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
|
|||||||
@Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
|
@Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
|
||||||
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
|
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
|
||||||
@Inject(PLATFORM_ID) private platformId: any,
|
@Inject(PLATFORM_ID) private platformId: any,
|
||||||
|
@Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject<boolean>,
|
||||||
private route: RouteService) {
|
private route: RouteService) {
|
||||||
super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig);
|
super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<h3>{{"search.filters.head" | translate}}</h3>
|
<h3>{{"search.filters.head" | translate}}</h3>
|
||||||
<div *ngIf="(filters | async)?.hasSucceeded">
|
<div *ngIf="(filters | async)?.hasSucceeded">
|
||||||
<div *ngFor="let filter of (filters | async)?.payload; trackBy: trackUpdate">
|
<div *ngFor="let filter of (filters | async)?.payload; trackBy: trackUpdate">
|
||||||
<ds-search-filter [filter]="filter" [inPlaceSearch]="inPlaceSearch"></ds-search-filter>
|
<ds-search-filter [filter]="filter" [inPlaceSearch]="inPlaceSearch" [refreshFilters]="refreshFilters"></ds-search-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-primary" [routerLink]="[searchLink]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button"><i class="fas fa-undo"></i> {{"search.filters.reset" | translate}}</a>
|
<a class="btn btn-primary" [routerLink]="[searchLink]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button"><i class="fas fa-undo"></i> {{"search.filters.reset" | translate}}</a>
|
||||||
|
@@ -53,7 +53,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Emits when the search filters values may be stale, and so they must be refreshed.
|
* Emits when the search filters values may be stale, and so they must be refreshed.
|
||||||
*/
|
*/
|
||||||
@Input() refreshFilters: Observable<any>;
|
@Input() refreshFilters: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link to the search page
|
* Link to the search page
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<h2 *ngIf="!disableHeader">{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}</h2>
|
<h2 *ngIf="!disableHeader">{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}</h2>
|
||||||
<ds-search-export-csv [searchConfig]="searchConfig"></ds-search-export-csv>
|
<ds-search-export-csv [searchConfig]="searchConfig"></ds-search-export-csv>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="searchResults && searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0" @fadeIn>
|
<div *ngIf="searchResults && searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0" @fadeIn>
|
||||||
<ds-viewable-collection
|
<ds-viewable-collection
|
||||||
@@ -13,14 +13,13 @@
|
|||||||
[linkType]="linkType"
|
[linkType]="linkType"
|
||||||
[context]="context"
|
[context]="context"
|
||||||
[hidePaginationDetail]="hidePaginationDetail"
|
[hidePaginationDetail]="hidePaginationDetail"
|
||||||
|
(contentChange)="contentChange.emit($event)"
|
||||||
(deselectObject)="deselectObject.emit($event)"
|
(deselectObject)="deselectObject.emit($event)"
|
||||||
(selectObject)="selectObject.emit($event)"
|
(selectObject)="selectObject.emit($event)">
|
||||||
>
|
|
||||||
</ds-viewable-collection>
|
</ds-viewable-collection>
|
||||||
</div>
|
</div>
|
||||||
<ds-themed-loading *ngIf="isLoading()" message="{{'loading.search-results' | translate}}"></ds-themed-loading>
|
<ds-themed-loading *ngIf="isLoading()" message="{{'loading.search-results' | translate}}"></ds-themed-loading>
|
||||||
<ds-error
|
<ds-error *ngIf="showError()"
|
||||||
*ngIf="showError()"
|
|
||||||
message="{{errorMessageLabel() | translate}}"></ds-error>
|
message="{{errorMessageLabel() | translate}}"></ds-error>
|
||||||
<div *ngIf="searchResults?.payload?.page.length == 0 || searchResults?.statusCode == 400">
|
<div *ngIf="searchResults?.payload?.page.length == 0 || searchResults?.statusCode == 400">
|
||||||
{{ 'search.results.no-results' | translate }}
|
{{ 'search.results.no-results' | translate }}
|
||||||
|
@@ -85,6 +85,11 @@ export class SearchResultsComponent {
|
|||||||
*/
|
*/
|
||||||
@Input() selectionConfig: SelectionConfig = null;
|
@Input() selectionConfig: SelectionConfig = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when one of the listed object has changed.
|
||||||
|
*/
|
||||||
|
@Output() contentChange = new EventEmitter<any>();
|
||||||
|
|
||||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
@@ -21,7 +21,7 @@ import { ListableObject } from '../../object-collection/shared/listable-object.m
|
|||||||
templateUrl: '../../theme-support/themed.component.html',
|
templateUrl: '../../theme-support/themed.component.html',
|
||||||
})
|
})
|
||||||
export class ThemedSearchResultsComponent extends ThemedComponent<SearchResultsComponent> {
|
export class ThemedSearchResultsComponent extends ThemedComponent<SearchResultsComponent> {
|
||||||
protected inAndOutputNames: (keyof SearchResultsComponent & keyof this)[] = ['linkType', 'searchResults', 'searchConfig', 'sortConfig', 'viewMode', 'configuration', 'disableHeader', 'selectable', 'context', 'hidePaginationDetail', 'selectionConfig', 'deselectObject', 'selectObject'];
|
protected inAndOutputNames: (keyof SearchResultsComponent & keyof this)[] = ['linkType', 'searchResults', 'searchConfig', 'sortConfig', 'viewMode', 'configuration', 'disableHeader', 'selectable', 'context', 'hidePaginationDetail', 'selectionConfig', 'contentChange', 'deselectObject', 'selectObject'];
|
||||||
|
|
||||||
@Input() linkType: CollectionElementLinkType;
|
@Input() linkType: CollectionElementLinkType;
|
||||||
|
|
||||||
@@ -45,6 +45,8 @@ export class ThemedSearchResultsComponent extends ThemedComponent<SearchResultsC
|
|||||||
|
|
||||||
@Input() selectionConfig: SelectionConfig = null;
|
@Input() selectionConfig: SelectionConfig = null;
|
||||||
|
|
||||||
|
@Output() contentChange: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
@@ -83,7 +83,7 @@ export class SearchSidebarComponent {
|
|||||||
/**
|
/**
|
||||||
* Emits when the search filters values may be stale, and so they must be refreshed.
|
* Emits when the search filters values may be stale, and so they must be refreshed.
|
||||||
*/
|
*/
|
||||||
@Input() refreshFilters: Observable<any>;
|
@Input() refreshFilters: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits event when the user clicks a button to open or close the sidebar
|
* Emits event when the user clicks a button to open or close the sidebar
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
[context]="(currentContext$ | async)"
|
[context]="(currentContext$ | async)"
|
||||||
[selectable]="selectable"
|
[selectable]="selectable"
|
||||||
[selectionConfig]="selectionConfig"
|
[selectionConfig]="selectionConfig"
|
||||||
|
(contentChange)="onContentChange($event)"
|
||||||
(deselectObject)="deselectObject.emit($event)"
|
(deselectObject)="deselectObject.emit($event)"
|
||||||
(selectObject)="selectObject.emit($event)"></ds-themed-search-results>
|
(selectObject)="selectObject.emit($event)"></ds-themed-search-results>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
[configuration]="(currentConfiguration$ | async)"
|
[configuration]="(currentConfiguration$ | async)"
|
||||||
[currentScope]="(currentScope$ | async)"
|
[currentScope]="(currentScope$ | async)"
|
||||||
[filters]="filtersRD$.asObservable()"
|
[filters]="filtersRD$.asObservable()"
|
||||||
|
[refreshFilters]="refreshFilters"
|
||||||
[resultCount]="(resultsRD$ | async)?.payload?.totalElements"
|
[resultCount]="(resultsRD$ | async)?.payload?.totalElements"
|
||||||
[searchOptions]="(searchOptions$ | async)"
|
[searchOptions]="(searchOptions$ | async)"
|
||||||
[sortOptionsList]="(sortOptionsList$ | async)"
|
[sortOptionsList]="(sortOptionsList$ | async)"
|
||||||
@@ -63,6 +65,7 @@
|
|||||||
[configuration]="(currentConfiguration$ | async)"
|
[configuration]="(currentConfiguration$ | async)"
|
||||||
[currentScope]="(currentScope$ | async)"
|
[currentScope]="(currentScope$ | async)"
|
||||||
[filters]="filtersRD$.asObservable()"
|
[filters]="filtersRD$.asObservable()"
|
||||||
|
[refreshFilters]="refreshFilters"
|
||||||
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
||||||
[searchOptions]="(searchOptions$ | async)"
|
[searchOptions]="(searchOptions$ | async)"
|
||||||
[sortOptionsList]="(sortOptionsList$ | async)"
|
[sortOptionsList]="(sortOptionsList$ | async)"
|
||||||
|
@@ -201,6 +201,11 @@ export class SearchComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
isXsOrSm$: Observable<boolean>;
|
isXsOrSm$: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits when the search filters values may be stale, and so they must be refreshed.
|
||||||
|
*/
|
||||||
|
refreshFilters: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link to the search page
|
* Link to the search page
|
||||||
*/
|
*/
|
||||||
@@ -339,6 +344,15 @@ export class SearchComponent implements OnInit {
|
|||||||
this.sidebarService.expand();
|
this.sidebarService.expand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit event to refresh filter content
|
||||||
|
* @param $event
|
||||||
|
*/
|
||||||
|
public onContentChange($event: any) {
|
||||||
|
this.retrieveFilters(this.lastSearchOptions);
|
||||||
|
this.refreshFilters.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe from the subscription
|
* Unsubscribe from the subscription
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user