mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
fixing more routing issues
This commit is contained in:
@@ -3,4 +3,4 @@
|
|||||||
[configuration$]="configuration$"
|
[configuration$]="configuration$"
|
||||||
[searchEnabled]="searchEnabled"
|
[searchEnabled]="searchEnabled"
|
||||||
[sideBarWidth]="sideBarWidth">
|
[sideBarWidth]="sideBarWidth">
|
||||||
</ds-filtered-search-page>
|
</ds-filtered-search-page>
|
@@ -1,9 +1,9 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { SearchFixedFilterService } from '../../../../core/shared/search/search-fixed-filter.service';
|
|
||||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||||
import { of } from 'rxjs/internal/observable/of';
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
import { getFilterByRelation } from '../../../../shared/utils/relation-query.utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-related-entities-search',
|
selector: 'ds-related-entities-search',
|
||||||
@@ -49,12 +49,12 @@ export class RelatedEntitiesSearchComponent implements OnInit {
|
|||||||
fixedFilter: string;
|
fixedFilter: string;
|
||||||
configuration$: Observable<string>;
|
configuration$: Observable<string>;
|
||||||
|
|
||||||
constructor(private fixedFilterService: SearchFixedFilterService) {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) {
|
if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) {
|
||||||
this.fixedFilter = this.fixedFilterService.getFilterByRelation(this.relationType, this.item.id);
|
this.fixedFilter = getFilterByRelation(this.relationType, this.item.id);
|
||||||
}
|
}
|
||||||
if (isNotEmpty(this.relationEntityType)) {
|
if (isNotEmpty(this.relationEntityType)) {
|
||||||
this.configuration$ = of(this.relationEntityType);
|
this.configuration$ = of(this.relationEntityType);
|
||||||
|
@@ -11,7 +11,6 @@ import { SearchConfigurationService } from '../core/shared/search/search-configu
|
|||||||
import { RouteService } from '../shared/services/route.service';
|
import { RouteService } from '../shared/services/route.service';
|
||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
import { SearchFixedFilterService } from '../core/shared/search/search-fixed-filter.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all actions that have to do with the current mydspace configuration
|
* Service that performs all actions that have to do with the current mydspace configuration
|
||||||
@@ -60,11 +59,10 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
|
|||||||
* @param {ActivatedRoute} route
|
* @param {ActivatedRoute} route
|
||||||
*/
|
*/
|
||||||
constructor(protected roleService: RoleService,
|
constructor(protected roleService: RoleService,
|
||||||
protected fixedFilterService: SearchFixedFilterService,
|
|
||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected route: ActivatedRoute) {
|
protected route: ActivatedRoute) {
|
||||||
|
|
||||||
super(routeService, fixedFilterService, route);
|
super(routeService, route);
|
||||||
|
|
||||||
// override parent class initialization
|
// override parent class initialization
|
||||||
this._defaults = null;
|
this._defaults = null;
|
||||||
|
@@ -3,14 +3,12 @@ import { SearchPageComponent } from './search-page.component';
|
|||||||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
||||||
import { pushInOut } from '../shared/animations/push';
|
import { pushInOut } from '../shared/animations/push';
|
||||||
import { RouteService } from '../shared/services/route.service';
|
import { RouteService } from '../shared/services/route.service';
|
||||||
import { Observable } 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 { map } from 'rxjs/operators';
|
|
||||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||||
import { SearchService } from '../core/shared/search/search.service';
|
import { SearchService } from '../core/shared/search/search.service';
|
||||||
import { SearchSidebarService } from '../core/shared/search/search-sidebar.service';
|
import { SearchSidebarService } from '../core/shared/search/search-sidebar.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
import { hasValue } from '../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a search page using a configuration as input.
|
* This component renders a search page using a configuration as input.
|
||||||
@@ -54,20 +52,8 @@ export class ConfigurationSearchPageComponent extends SearchPageComponent implem
|
|||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
if (hasValue(this.configuration )) {
|
||||||
|
this.routeService.setParameter('configuration', this.configuration);
|
||||||
/**
|
}
|
||||||
* Get the current paginated search options after updating the configuration using the configuration input
|
|
||||||
* This is to make sure the configuration is included in the paginated search options, as it is not part of any
|
|
||||||
* query or route parameters
|
|
||||||
* @returns {Observable<PaginatedSearchOptions>}
|
|
||||||
*/
|
|
||||||
protected getSearchOptions(): Observable<PaginatedSearchOptions> {
|
|
||||||
return this.searchConfigService.paginatedSearchOptions.pipe(
|
|
||||||
map((options: PaginatedSearchOptions) => {
|
|
||||||
const config = this.configuration || options.configuration;
|
|
||||||
return Object.assign(options, { configuration: config });
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,7 +35,7 @@ import { hasValue, isNotEmpty, isNotEmptyOperator } from '../shared/empty.util';
|
|||||||
export class FilteredSearchPageComponent extends SearchPageComponent implements OnInit {
|
export class FilteredSearchPageComponent extends SearchPageComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* The actual query for the fixed filter.
|
* The actual query for the fixed filter.
|
||||||
* If empty, the query will be determined by the route parameter called 'filter'
|
* If empty, the query will be determined by the route parameter called 'fixedFilterQuery'
|
||||||
*/
|
*/
|
||||||
@Input() fixedFilterQuery: string;
|
@Input() fixedFilterQuery: string;
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ export class FilteredSearchPageComponent extends SearchPageComponent implements
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
if (hasValue(this.fixedFilterQuery)) {
|
if (hasValue(this.fixedFilterQuery)) {
|
||||||
this.routeService.setParameter('filterQuery', this.fixedFilterQuery);
|
this.routeService.setParameter('fixedFilterQuery', this.fixedFilterQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,13 +4,15 @@ import { CoreModule } from '../core/core.module';
|
|||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { SearchPageRoutingModule } from './search-page-routing.module';
|
import { SearchPageRoutingModule } from './search-page-routing.module';
|
||||||
import { SearchPageComponent } from './search-page.component';
|
import { SearchPageComponent } from './search-page.component';
|
||||||
import { FilteredSearchPageComponent } from './filtered-search-page.component';
|
|
||||||
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
||||||
|
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||||
|
import { FilteredSearchPageComponent } from './filtered-search-page.component';
|
||||||
|
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
SearchPageComponent,
|
SearchPageComponent,
|
||||||
FilteredSearchPageComponent
|
FilteredSearchPageComponent,
|
||||||
|
ConfigurationSearchPageComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { ObjectCacheEffects } from './cache/object-cache.effects';
|
import { ObjectCacheEffects } from './cache/object-cache.effects';
|
||||||
import { UUIDIndexEffects } from './index/index.effects';
|
import { UUIDIndexEffects } from './index/index.effects';
|
||||||
import { RequestEffects } from './data/request.effects';
|
import { RequestEffects } from './data/request.effects';
|
||||||
@@ -7,6 +6,7 @@ import { JsonPatchOperationsEffects } from './json-patch/json-patch-operations.e
|
|||||||
import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
||||||
import { ObjectUpdatesEffects } from './data/object-updates/object-updates.effects';
|
import { ObjectUpdatesEffects } from './data/object-updates/object-updates.effects';
|
||||||
import { RouteEffects } from '../shared/services/route.effects';
|
import { RouteEffects } from '../shared/services/route.effects';
|
||||||
|
import { RouterEffects } from './router/router.effects';
|
||||||
|
|
||||||
export const coreEffects = [
|
export const coreEffects = [
|
||||||
RequestEffects,
|
RequestEffects,
|
||||||
@@ -16,5 +16,6 @@ export const coreEffects = [
|
|||||||
JsonPatchOperationsEffects,
|
JsonPatchOperationsEffects,
|
||||||
ServerSyncBufferEffects,
|
ServerSyncBufferEffects,
|
||||||
ObjectUpdatesEffects,
|
ObjectUpdatesEffects,
|
||||||
RouteEffects
|
RouteEffects,
|
||||||
|
RouterEffects
|
||||||
];
|
];
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
import {
|
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
|
||||||
ModuleWithProviders,
|
|
||||||
NgModule,
|
|
||||||
Optional,
|
|
||||||
SkipSelf
|
|
||||||
} from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { StoreModule } from '@ngrx/store';
|
import { StoreModule } from '@ngrx/store';
|
||||||
import { EffectsModule } from '@ngrx/effects';
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
|
import {
|
||||||
|
DynamicFormLayoutService,
|
||||||
|
DynamicFormService,
|
||||||
|
DynamicFormValidationService
|
||||||
|
} from '@ng-dynamic-forms/core';
|
||||||
|
|
||||||
import { coreEffects } from './core.effects';
|
import { coreEffects } from './core.effects';
|
||||||
import { coreReducers } from './core.reducers';
|
import { coreReducers } from './core.reducers';
|
||||||
@@ -94,10 +93,9 @@ import {
|
|||||||
mockResponseMap
|
mockResponseMap
|
||||||
} from './dspace-rest-v2/mocks/mock-response-map';
|
} from './dspace-rest-v2/mocks/mock-response-map';
|
||||||
import { EndpointMockingRestService } from './dspace-rest-v2/endpoint-mocking-rest.service';
|
import { EndpointMockingRestService } from './dspace-rest-v2/endpoint-mocking-rest.service';
|
||||||
import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../../config';
|
import { GLOBAL_CONFIG, GlobalConfig } from '../../config';
|
||||||
import { SearchSidebarService } from './shared/search/search-sidebar.service';
|
import { SearchSidebarService } from './shared/search/search-sidebar.service';
|
||||||
import { SearchFilterService } from './shared/search/search-filter.service';
|
import { SearchFilterService } from './shared/search/search-filter.service';
|
||||||
import { SearchFixedFilterService } from './shared/search/search-fixed-filter.service';
|
|
||||||
import { SearchConfigurationService } from './shared/search/search-configuration.service';
|
import { SearchConfigurationService } from './shared/search/search-configuration.service';
|
||||||
import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service';
|
import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service';
|
||||||
|
|
||||||
@@ -201,7 +199,6 @@ const PROVIDERS = [
|
|||||||
SearchService,
|
SearchService,
|
||||||
SearchSidebarService,
|
SearchSidebarService,
|
||||||
SearchFilterService,
|
SearchFilterService,
|
||||||
SearchFixedFilterService,
|
|
||||||
SearchFilterService,
|
SearchFilterService,
|
||||||
SearchConfigurationService,
|
SearchConfigurationService,
|
||||||
SelectableListService,
|
SelectableListService,
|
||||||
|
29
src/app/core/router/router.actions.ts
Normal file
29
src/app/core/router/router.actions.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
import { type } from '../../shared/ngrx/type';
|
||||||
|
import { Params } from '@angular/router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of HrefIndexAction type definitions
|
||||||
|
*/
|
||||||
|
export const RouterActionTypes = {
|
||||||
|
ROUTE_UPDATE: type('dspace/core/router/ROUTE_UPDATE'),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
/**
|
||||||
|
* An ngrx action to be fired when the route is updated
|
||||||
|
* Note that, contrary to the router-store.ROUTER_NAVIGATION action,
|
||||||
|
* this action will only be fired when the path changes,
|
||||||
|
* not when just the query parameters change
|
||||||
|
*/
|
||||||
|
export class RouteUpdateAction implements Action {
|
||||||
|
type = RouterActionTypes.ROUTE_UPDATE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new RouteUpdateAction
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tslint:enable:max-classes-per-file */
|
31
src/app/core/router/router.effects.ts
Normal file
31
src/app/core/router/router.effects.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { filter, map, pairwise } from 'rxjs/operators';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Actions, Effect, ofType } from '@ngrx/effects'
|
||||||
|
import * as fromRouter from '@ngrx/router-store';
|
||||||
|
import { RouterNavigationAction } from '@ngrx/router-store';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouteUpdateAction } from './router.actions';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RouterEffects {
|
||||||
|
/**
|
||||||
|
* Effect that fires a new RouteUpdateAction when then path of route is changed
|
||||||
|
* @type {Observable<RouteUpdateAction>}
|
||||||
|
*/
|
||||||
|
@Effect() routeChange$ = this.actions$
|
||||||
|
.pipe(
|
||||||
|
ofType(fromRouter.ROUTER_NAVIGATION),
|
||||||
|
pairwise(),
|
||||||
|
map((actions: RouterNavigationAction[]) =>
|
||||||
|
actions.map((navigateAction) => {
|
||||||
|
const urlTree = this.router.parseUrl(navigateAction.payload.routerState.url);
|
||||||
|
return urlTree.root.children['primary'].segments.map(it => it.path).join('/');
|
||||||
|
})),
|
||||||
|
filter((actions: string[]) => actions[0] !== actions[1]),
|
||||||
|
map(() => new RouteUpdateAction())
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(private actions$: Actions, private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -167,8 +167,8 @@ describe('SearchConfigurationService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service.getCurrentFixedFilter();
|
service.getCurrentFixedFilter();
|
||||||
});
|
});
|
||||||
it('should call getRouteParameterValue on the routeService with parameter name \'filter\'', () => {
|
it('should call getRouteParameterValue on the routeService with parameter name \'fixedFilterQuery\'', () => {
|
||||||
expect((service as any).routeService.getRouteParameterValue).toHaveBeenCalledWith('filter');
|
expect((service as any).routeService.getRouteParameterValue).toHaveBeenCalledWith('fixedFilterQuery');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable, OnDestroy } from '@angular/core';
|
import {Injectable, OnDestroy} from '@angular/core';
|
||||||
import { ActivatedRoute, Params } from '@angular/router';
|
import {ActivatedRoute, Params} from '@angular/router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
@@ -9,18 +9,17 @@ import {
|
|||||||
of as observableOf,
|
of as observableOf,
|
||||||
Subscription
|
Subscription
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { filter, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
|
import {filter, map, startWith, tap} from 'rxjs/operators';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import {PaginationComponentOptions} from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { SearchOptions } from '../../../shared/search/search-options.model';
|
import {SearchOptions} from '../../../shared/search/search-options.model';
|
||||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
import {PaginatedSearchOptions} from '../../../shared/search/paginated-search-options.model';
|
||||||
import { RouteService } from '../../../shared/services/route.service';
|
import {RouteService} from '../../../shared/services/route.service';
|
||||||
import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../shared/empty.util';
|
import {hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator} from '../../../shared/empty.util';
|
||||||
import { SearchFilter } from '../../../shared/search/search-filter.model';
|
import {SearchFilter} from '../../../shared/search/search-filter.model';
|
||||||
import { SearchFixedFilterService } from './search-fixed-filter.service';
|
import {RemoteData} from '../../data/remote-data';
|
||||||
import { SortDirection, SortOptions } from '../../cache/models/sort-options.model';
|
import {getSucceededRemoteData} from '../operators';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import {DSpaceObjectType} from '../dspace-object-type.model';
|
||||||
import { getSucceededRemoteData } from '../operators';
|
import {SortDirection, SortOptions} from '../../cache/models/sort-options.model';
|
||||||
import { DSpaceObjectType } from '../dspace-object-type.model';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all actions that have to do with the current search configuration
|
* Service that performs all actions that have to do with the current search configuration
|
||||||
@@ -83,7 +82,6 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
* @param {ActivatedRoute} route
|
* @param {ActivatedRoute} route
|
||||||
*/
|
*/
|
||||||
constructor(protected routeService: RouteService,
|
constructor(protected routeService: RouteService,
|
||||||
protected fixedFilterService: SearchFixedFilterService,
|
|
||||||
protected route: ActivatedRoute) {
|
protected route: ActivatedRoute) {
|
||||||
|
|
||||||
this.initDefaults();
|
this.initDefaults();
|
||||||
@@ -208,10 +206,7 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
* @returns {Observable<string>} Emits the current fixed filter as a string
|
* @returns {Observable<string>} Emits the current fixed filter as a string
|
||||||
*/
|
*/
|
||||||
getCurrentFixedFilter(): Observable<string> {
|
getCurrentFixedFilter(): Observable<string> {
|
||||||
return this.routeService.getRouteParameterValue('filter').pipe(
|
return this.routeService.getRouteParameterValue('fixedFilterQuery').pipe(tap((t) => console.log(t)));
|
||||||
switchMap((f) => this.fixedFilterService.getQueryByFilterName(f)),
|
|
||||||
tap((t) => console.log(t))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -265,7 +265,7 @@ describe('SearchFilterService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when the getCurrentFixedFilter method is called', () => {
|
describe('when the getCurrentFixedFilter method is called', () => {
|
||||||
const filter = 'filter';
|
const filter = 'fixedFilterQuery';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(routeServiceStub, 'getRouteParameterValue').and.returnValue(observableOf(filter));
|
spyOn(routeServiceStub, 'getRouteParameterValue').and.returnValue(observableOf(filter));
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
import { mergeMap, map, distinctUntilChanged } from 'rxjs/operators';
|
import { distinctUntilChanged, map, mergeMap } from 'rxjs/operators';
|
||||||
import { Injectable, InjectionToken } from '@angular/core';
|
import { Injectable, InjectionToken } from '@angular/core';
|
||||||
import { SearchFiltersState, SearchFilterState } from '../../../shared/search/search-filters/search-filter/search-filter.reducer';
|
import {
|
||||||
|
SearchFiltersState,
|
||||||
|
SearchFilterState
|
||||||
|
} from '../../../shared/search/search-filters/search-filter/search-filter.reducer';
|
||||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import {
|
import {
|
||||||
SearchFilterCollapseAction,
|
SearchFilterCollapseAction,
|
||||||
@@ -17,12 +20,7 @@ import { SearchFilterConfig } from '../../../shared/search/search-filter-config.
|
|||||||
import { RouteService } from '../../../shared/services/route.service';
|
import { RouteService } from '../../../shared/services/route.service';
|
||||||
import { SortDirection, SortOptions } from '../../cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../cache/models/sort-options.model';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { SearchOptions } from '../../../shared/search/search-options.model';
|
|
||||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
|
||||||
import { SearchFixedFilterService } from './search-fixed-filter.service';
|
|
||||||
import { Params } from '@angular/router';
|
import { Params } from '@angular/router';
|
||||||
import * as postcss from 'postcss';
|
|
||||||
import prefix = postcss.vendor.prefix;
|
|
||||||
// const spy = create();
|
// const spy = create();
|
||||||
const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
|
const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
|
||||||
|
|
||||||
@@ -36,8 +34,7 @@ export const IN_PLACE_SEARCH: InjectionToken<boolean> = new InjectionToken<boole
|
|||||||
export class SearchFilterService {
|
export class SearchFilterService {
|
||||||
|
|
||||||
constructor(private store: Store<SearchFiltersState>,
|
constructor(private store: Store<SearchFiltersState>,
|
||||||
private routeService: RouteService,
|
private routeService: RouteService) {
|
||||||
private fixedFilterService: SearchFixedFilterService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,8 +119,7 @@ export class SearchFilterService {
|
|||||||
* @returns {Observable<string>}
|
* @returns {Observable<string>}
|
||||||
*/
|
*/
|
||||||
getCurrentFixedFilter(): Observable<string> {
|
getCurrentFixedFilter(): Observable<string> {
|
||||||
const filter: Observable<string> = this.routeService.getRouteParameterValue('filter');
|
return this.routeService.getRouteParameterValue('fixedFilterQuery');
|
||||||
return filter.pipe(mergeMap((f) => this.fixedFilterService.getQueryByFilterName(f)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,59 +0,0 @@
|
|||||||
import { SearchFixedFilterService } from './search-fixed-filter.service';
|
|
||||||
import { RouteService } from '../../../shared/services/route.service';
|
|
||||||
import { RequestService } from '../../data/request.service';
|
|
||||||
import { HALEndpointService } from '../hal-endpoint.service';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { RequestEntry } from '../../data/request.reducer';
|
|
||||||
import { FilteredDiscoveryQueryResponse, RestResponse } from '../../cache/response.models';
|
|
||||||
|
|
||||||
describe('SearchFixedFilterService', () => {
|
|
||||||
let service: SearchFixedFilterService;
|
|
||||||
|
|
||||||
const filterQuery = 'filter:query';
|
|
||||||
|
|
||||||
const routeServiceStub = {} as RouteService;
|
|
||||||
const requestServiceStub = Object.assign({
|
|
||||||
/* tslint:disable:no-empty */
|
|
||||||
configure: () => {},
|
|
||||||
/* tslint:enable:no-empty */
|
|
||||||
generateRequestId: () => 'fake-id',
|
|
||||||
getByHref: () => observableOf(Object.assign(new RequestEntry(), {
|
|
||||||
response: new FilteredDiscoveryQueryResponse(filterQuery, 200, 'OK')
|
|
||||||
}))
|
|
||||||
}) as RequestService;
|
|
||||||
const halServiceStub = Object.assign(new HALEndpointService(requestServiceStub, undefined), {
|
|
||||||
getEndpoint: () => observableOf('fake-url')
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
service = new SearchFixedFilterService(routeServiceStub, requestServiceStub, halServiceStub);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when getQueryByFilterName is called with a filterName', () => {
|
|
||||||
it('should return the filter query', () => {
|
|
||||||
service.getQueryByFilterName('filter').subscribe((query) => {
|
|
||||||
expect(query).toBe(filterQuery);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when getQueryByFilterName is called without a filterName', () => {
|
|
||||||
it('should return undefined', () => {
|
|
||||||
service.getQueryByFilterName(undefined).subscribe((query) => {
|
|
||||||
expect(query).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when getQueryByRelations is called', () => {
|
|
||||||
const relationType = 'isRelationOf';
|
|
||||||
const itemUUID = 'c5b277e6-2477-48bb-8993-356710c285f3';
|
|
||||||
|
|
||||||
it('should contain the relationType and itemUUID', () => {
|
|
||||||
const query = service.getQueryByRelations(relationType, itemUUID);
|
|
||||||
expect(query.length).toBeGreaterThan(relationType.length + itemUUID.length);
|
|
||||||
expect(query).toContain(relationType);
|
|
||||||
expect(query).toContain(itemUUID);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,80 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { flatMap, map, switchMap, tap } from 'rxjs/operators';
|
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
|
||||||
import { HALEndpointService } from '../hal-endpoint.service';
|
|
||||||
import { GetRequest, RestRequest } from '../../data/request.models';
|
|
||||||
import { RequestService } from '../../data/request.service';
|
|
||||||
import { ResponseParsingService } from '../../data/parsing.service';
|
|
||||||
import { GenericConstructor } from '../generic-constructor';
|
|
||||||
import { FilteredDiscoveryPageResponseParsingService } from '../../data/filtered-discovery-page-response-parsing.service';
|
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
|
||||||
import { configureRequest, getResponseFromEntry } from '../operators';
|
|
||||||
import { RouteService } from '../../../shared/services/route.service';
|
|
||||||
import { FilteredDiscoveryQueryResponse } from '../../cache/response.models';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Service for performing actions on the filtered-discovery-pages REST endpoint
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class SearchFixedFilterService {
|
|
||||||
private queryByFilterPath = 'filtered-discovery-pages';
|
|
||||||
|
|
||||||
constructor(private routeService: RouteService,
|
|
||||||
protected requestService: RequestService,
|
|
||||||
private halService: HALEndpointService) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the filter query for a certain filter by name
|
|
||||||
* @param {string} filterName Name of the filter
|
|
||||||
* @returns {Observable<string>} Filter query
|
|
||||||
*/
|
|
||||||
getQueryByFilterName(filterName: string): Observable<string> {
|
|
||||||
if (hasValue(filterName)) {
|
|
||||||
const requestUuid = this.requestService.generateRequestId();
|
|
||||||
const requestObs = this.halService.getEndpoint(this.queryByFilterPath).pipe(
|
|
||||||
map((url: string) => {
|
|
||||||
url += ('/' + filterName);
|
|
||||||
const request = new GetRequest(requestUuid, url);
|
|
||||||
return Object.assign(request, {
|
|
||||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
|
||||||
return FilteredDiscoveryPageResponseParsingService;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
configureRequest(this.requestService)
|
|
||||||
);
|
|
||||||
|
|
||||||
const requestEntryObs = requestObs.pipe(
|
|
||||||
switchMap((request: RestRequest) => this.requestService.getByHref(request.href)),
|
|
||||||
);
|
|
||||||
const filterQuery = requestEntryObs.pipe(
|
|
||||||
getResponseFromEntry(),
|
|
||||||
map((response: FilteredDiscoveryQueryResponse) =>
|
|
||||||
response.filterQuery
|
|
||||||
));
|
|
||||||
return filterQuery;
|
|
||||||
}
|
|
||||||
return observableOf(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the query for looking up items by relation type
|
|
||||||
* @param {string} relationType Relation type
|
|
||||||
* @param {string} itemUUID Item UUID
|
|
||||||
* @returns {string} Query
|
|
||||||
*/
|
|
||||||
getQueryByRelations(relationType: string, itemUUID: string): string {
|
|
||||||
return `query=relation.${relationType}:${itemUUID}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the filter for a relation with the item's UUID
|
|
||||||
* @param relationType The type of relation e.g. 'isAuthorOfPublication'
|
|
||||||
* @param itemUUID The item's UUID
|
|
||||||
*/
|
|
||||||
getFilterByRelation(relationType: string, itemUUID: string): string {
|
|
||||||
return `f.${relationType}=${itemUUID}`;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,13 +3,13 @@ import { Observable , of as observableOf } from 'rxjs';
|
|||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||||
import { SearchFixedFilterService } from '../../../../core/shared/search/search-fixed-filter.service';
|
|
||||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||||
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
|
||||||
import {
|
import {
|
||||||
filterRelationsByTypeLabel,
|
filterRelationsByTypeLabel,
|
||||||
relationsToItems
|
relationsToItems
|
||||||
} from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
} from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||||
|
import { getQueryByRelations } from '../../../../shared/utils/relation-query.utils';
|
||||||
|
|
||||||
@rendersItemType('Person', ItemViewMode.Full)
|
@rendersItemType('Person', ItemViewMode.Full)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -47,8 +47,7 @@ export class PersonComponent extends ItemComponent {
|
|||||||
fixedFilterQuery: string;
|
fixedFilterQuery: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(ITEM) public item: Item,
|
@Inject(ITEM) public item: Item
|
||||||
private fixedFilterService: SearchFixedFilterService
|
|
||||||
) {
|
) {
|
||||||
super(item);
|
super(item);
|
||||||
}
|
}
|
||||||
@@ -71,7 +70,7 @@ export class PersonComponent extends ItemComponent {
|
|||||||
relationsToItems(this.item.id)
|
relationsToItems(this.item.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id);
|
this.fixedFilterQuery = getQueryByRelations('isAuthorOfPublication', this.item.id);
|
||||||
this.fixedFilter$ = observableOf('publication');
|
this.fixedFilter$ = observableOf('publication');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,9 +52,8 @@ export class DsDynamicLookupRelationModalComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.resetRoute();
|
this.resetRoute();
|
||||||
this.fixedFilter = RELATION_TYPE_FILTER_PREFIX + this.fieldName;
|
this.fixedFilter = RELATION_TYPE_FILTER_PREFIX + this.fieldName;
|
||||||
|
this.routeService.setParameter('fixedFilterQuery', this.fixedFilter);
|
||||||
|
|
||||||
///Throw away, this is just a hack for now
|
|
||||||
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((t) => this.routeService.setParameter('filterQuery', this.fixedFilter));
|
|
||||||
this.selection = this.selectableListService.getSelectableList(this.listId).pipe(map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []));
|
this.selection = this.selectableListService.getSelectableList(this.listId).pipe(map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []));
|
||||||
this.resultsRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
|
this.resultsRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
|
||||||
map((options) => {
|
map((options) => {
|
||||||
|
@@ -56,7 +56,6 @@ export class SearchFiltersComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.filters = this.searchConfigService.searchOptions.pipe(
|
this.filters = this.searchConfigService.searchOptions.pipe(
|
||||||
switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getSucceededRemoteData())),
|
switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getSucceededRemoteData())),
|
||||||
obsLog('searchoptions')
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
|
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
import { filter, map, pairwise } from 'rxjs/operators';
|
import { map, tap } from 'rxjs/operators';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Actions, Effect, ofType } from '@ngrx/effects'
|
import { Actions, Effect, ofType } from '@ngrx/effects'
|
||||||
import * as fromRouter from '@ngrx/router-store';
|
import { ResetRouteStateAction, RouteActionTypes } from './route.actions';
|
||||||
import { RouterNavigationAction } from '@ngrx/router-store';
|
import { RouterActionTypes } from '../../core/router/router.actions';
|
||||||
import { ResetRouteStateAction } from './route.actions';
|
import { RouteService } from './route.service';
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RouteEffects {
|
export class RouteEffects {
|
||||||
@@ -14,19 +13,16 @@ export class RouteEffects {
|
|||||||
*/
|
*/
|
||||||
@Effect() routeChange$ = this.actions$
|
@Effect() routeChange$ = this.actions$
|
||||||
.pipe(
|
.pipe(
|
||||||
ofType(fromRouter.ROUTER_NAVIGATION),
|
ofType(RouterActionTypes.ROUTE_UPDATE),
|
||||||
pairwise(),
|
map(() => new ResetRouteStateAction()),
|
||||||
map((actions: RouterNavigationAction[]) =>
|
|
||||||
actions.map((navigateAction) => {
|
|
||||||
const urlTree = this.router.parseUrl(navigateAction.payload.routerState.url);
|
|
||||||
return urlTree.root.children['primary'].segments.map(it => it.path).join('/');
|
|
||||||
})),
|
|
||||||
filter((actions: string[]) => actions[0] !== actions[1]),
|
|
||||||
map(() => new ResetRouteStateAction())
|
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor(private actions$: Actions, private router: Router) {
|
@Effect({dispatch: false }) afterResetChange$ = this.actions$
|
||||||
|
.pipe(
|
||||||
|
ofType(RouteActionTypes.RESET),
|
||||||
|
tap(() => this.service.setCurrentRouteInfo()),
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(private actions$: Actions, private service: RouteService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -73,6 +73,7 @@ function addParameter(state: RouteState, action: AddParameterAction | AddQueryPa
|
|||||||
const newSubstate = Object.assign({}, subState, { [action.payload.key]: newValues });
|
const newSubstate = Object.assign({}, subState, { [action.payload.key]: newValues });
|
||||||
return Object.assign({}, state, { [paramType]: newSubstate });
|
return Object.assign({}, state, { [paramType]: newSubstate });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a route or query parameter in the store
|
* Set a route or query parameter in the store
|
||||||
* @param state The current state
|
* @param state The current state
|
||||||
@@ -80,7 +81,7 @@ function addParameter(state: RouteState, action: AddParameterAction | AddQueryPa
|
|||||||
* @param paramType The type of parameter to set: route or query parameter
|
* @param paramType The type of parameter to set: route or query parameter
|
||||||
*/
|
*/
|
||||||
function setParameters(state: RouteState, action: SetParametersAction | SetQueryParametersAction, paramType: string): RouteState {
|
function setParameters(state: RouteState, action: SetParametersAction | SetQueryParametersAction, paramType: string): RouteState {
|
||||||
return Object.assign({}, state, { [paramType]: action.payload });
|
return Object.assign({}, state, { [paramType]: { [action.payload.key]: action.payload.value } });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,6 +92,6 @@ function setParameters(state: RouteState, action: SetParametersAction | SetQuery
|
|||||||
*/
|
*/
|
||||||
function setParameter(state: RouteState, action: SetParameterAction | SetQueryParameterAction, paramType: string): RouteState {
|
function setParameter(state: RouteState, action: SetParameterAction | SetQueryParameterAction, paramType: string): RouteState {
|
||||||
const subState = state[paramType];
|
const subState = state[paramType];
|
||||||
const newSubstate = Object.assign({}, subState, action.payload);
|
const newSubstate = Object.assign({}, subState, { [action.payload.key]: action.payload.value });
|
||||||
return Object.assign({}, state, { [paramType]: newSubstate });
|
return Object.assign({}, state, { [paramType]: newSubstate });
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, take } from 'rxjs/operators';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRoute,
|
ActivatedRoute,
|
||||||
@@ -126,7 +126,7 @@ export class RouteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getRouteDataValue(datafield: string): Observable<any> {
|
getRouteDataValue(datafield: string): Observable<any> {
|
||||||
return this.route.data.pipe(map((data) => data[datafield]), distinctUntilChanged(),);
|
return this.route.data.pipe(map((data) => data[datafield]), distinctUntilChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -198,6 +198,7 @@ export class RouteService {
|
|||||||
|
|
||||||
public setCurrentRouteInfo() {
|
public setCurrentRouteInfo() {
|
||||||
combineLatest(this.getRouteParams(), this.route.queryParams)
|
combineLatest(this.getRouteParams(), this.route.queryParams)
|
||||||
|
.pipe(take(1))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
([params, queryParams]: [Params, Params]) => {
|
([params, queryParams]: [Params, Params]) => {
|
||||||
this.store.dispatch(new SetParametersAction(params));
|
this.store.dispatch(new SetParametersAction(params));
|
||||||
|
@@ -160,7 +160,6 @@ import { SearchFacetSelectedOptionComponent } from './search/search-filters/sear
|
|||||||
import { SearchFacetRangeOptionComponent } from './search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component';
|
import { SearchFacetRangeOptionComponent } from './search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component';
|
||||||
import { SearchSwitchConfigurationComponent } from './search/search-switch-configuration/search-switch-configuration.component';
|
import { SearchSwitchConfigurationComponent } from './search/search-switch-configuration/search-switch-configuration.component';
|
||||||
import { SearchAuthorityFilterComponent } from './search/search-filters/search-filter/search-authority-filter/search-authority-filter.component';
|
import { SearchAuthorityFilterComponent } from './search/search-filters/search-filter/search-authority-filter/search-authority-filter.component';
|
||||||
import { ConfigurationSearchPageComponent } from '../+search-page/configuration-search-page.component';
|
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||||
@@ -304,7 +303,6 @@ const COMPONENTS = [
|
|||||||
SearchFacetRangeOptionComponent,
|
SearchFacetRangeOptionComponent,
|
||||||
SearchSwitchConfigurationComponent,
|
SearchSwitchConfigurationComponent,
|
||||||
SearchAuthorityFilterComponent,
|
SearchAuthorityFilterComponent,
|
||||||
ConfigurationSearchPageComponent
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
|
18
src/app/shared/utils/relation-query.utils.ts
Normal file
18
src/app/shared/utils/relation-query.utils.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Get the query for looking up items by relation type
|
||||||
|
* @param {string} relationType Relation type
|
||||||
|
* @param {string} itemUUID Item UUID
|
||||||
|
* @returns {string} Query
|
||||||
|
*/
|
||||||
|
export function getQueryByRelations(relationType: string, itemUUID: string): string {
|
||||||
|
return `query=relation.${relationType}:${itemUUID}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the filter for a relation with the item's UUID
|
||||||
|
* @param relationType The type of relation e.g. 'isAuthorOfPublication'
|
||||||
|
* @param itemUUID The item's UUID
|
||||||
|
*/
|
||||||
|
export function getFilterByRelation(relationType: string, itemUUID: string): string {
|
||||||
|
return `f.${relationType}=${itemUUID}`;
|
||||||
|
}
|
Reference in New Issue
Block a user