Skip to content

Commit

Permalink
feat(openchallenges): implement lazy loading in searchable dropdown f…
Browse files Browse the repository at this point in the history
…ilters on search pages (#2637)

Co-authored-by: Thomas Schaffter <thomas.schaffter@gmail.com>
  • Loading branch information
rrchai and tschaffter authored May 16, 2024
1 parent 8a48c0f commit 8322a8f
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ChallengePlatformSort,
EdamConceptSearchQuery,
EdamConceptService,
EdamSection,
EdamConceptSort,
Image,
ImageAspectRatio,
ImageHeight,
Expand All @@ -26,19 +26,26 @@ import {
} from '@sagebionetworks/openchallenges/api-client-angular';
import { forkJoinConcurrent } from '@sagebionetworks/openchallenges/util';
import { Filter } from '@sagebionetworks/openchallenges/ui';
import { ChallengeSearchDropdown } from './challenge-search-dropdown';

@Injectable({
providedIn: 'root',
})
export class ChallengeSearchDataService {
private edamConceptSearchTerms: BehaviorSubject<EdamConceptSearchQuery> =
new BehaviorSubject<EdamConceptSearchQuery>({});
private edamConceptSearchQuery: BehaviorSubject<EdamConceptSearchQuery> =
new BehaviorSubject<EdamConceptSearchQuery>({
sort: EdamConceptSort.PreferredLabel,
});

private organizationSearchTerms: BehaviorSubject<string> =
new BehaviorSubject<string>('');
private organizationSearchQuery: BehaviorSubject<OrganizationSearchQuery> =
new BehaviorSubject<OrganizationSearchQuery>({
sort: OrganizationSort.Name,
});

private platformSearchTerms: BehaviorSubject<string> =
new BehaviorSubject<string>('');
private platformSearchQuery: BehaviorSubject<ChallengePlatformSearchQuery> =
new BehaviorSubject<ChallengePlatformSearchQuery>({
sort: ChallengePlatformSort.Name,
});

constructor(
private challengePlatformService: ChallengePlatformService,
Expand All @@ -47,26 +54,32 @@ export class ChallengeSearchDataService {
private organizationService: OrganizationService
) {}

setEdamConceptSearchTerms(searchQuery: EdamConceptSearchQuery) {
this.edamConceptSearchTerms.next(searchQuery);
setEdamConceptSearchQuery(searchQuery: EdamConceptSearchQuery) {
const currentState = this.edamConceptSearchQuery.getValue();
this.edamConceptSearchQuery.next({ ...currentState, ...searchQuery });
}

setOriganizationSearchTerms(searchTerms: string) {
this.organizationSearchTerms.next(searchTerms);
setOriganizationSearchQuery(searchQuery: OrganizationSearchQuery) {
const currentState = this.organizationSearchQuery.getValue();
this.organizationSearchQuery.next({ ...currentState, ...searchQuery });
}

setPlatformSearchTerms(searchTerms: string) {
this.platformSearchTerms.next(searchTerms);
setPlatformSearchQuery(searchQuery: ChallengePlatformSearchQuery) {
const currentState = this.platformSearchQuery.getValue();
this.platformSearchQuery.next({ ...currentState, ...searchQuery });
}

searchEdamConcepts(sections?: EdamSection): Observable<Filter[]> {
return this.edamConceptSearchTerms.pipe(
getEdamConcepts(newQuery: EdamConceptSearchQuery): Observable<Filter[]> {
return this.edamConceptSearchQuery.pipe(
debounceTime(400),
distinctUntilChanged(),
switchMap((searchQuery: EdamConceptSearchQuery) => {
searchQuery.sections = sections ? [sections] : searchQuery.sections;
return this.edamConceptService.listEdamConcepts(searchQuery);
}),
switchMap((searchQuery: EdamConceptSearchQuery) =>
// use the properties from new query to overwrite the ones from old query
this.edamConceptService.listEdamConcepts({
...searchQuery,
...newQuery,
})
),
map((page) =>
page.edamConcepts.map((edamConcept) => ({
value: edamConcept.id,
Expand All @@ -77,18 +90,16 @@ export class ChallengeSearchDataService {
);
}

searchOriganizations(): Observable<Filter[]> {
return this.organizationSearchTerms.pipe(
getOriganizations(newQuery: OrganizationSearchQuery): Observable<Filter[]> {
return this.organizationSearchQuery.pipe(
debounceTime(400),
distinctUntilChanged(),
switchMap((searchTerm: string) => {
const sortBy: OrganizationSort = 'name';
const orgQuery: OrganizationSearchQuery = {
searchTerms: searchTerm,
sort: sortBy,
};
return this.organizationService.listOrganizations(orgQuery);
}),
switchMap((searchQuery: OrganizationSearchQuery) =>
this.organizationService.listOrganizations({
...searchQuery,
...newQuery,
})
),
map((page) => page.organizations),
switchMap((orgs) =>
forkJoin({
Expand Down Expand Up @@ -129,19 +140,15 @@ export class ChallengeSearchDataService {
);
}

searchPlatforms(): Observable<Filter[]> {
return this.platformSearchTerms.pipe(
getPlatforms(newQuery: ChallengePlatformSearchQuery): Observable<Filter[]> {
return this.platformSearchQuery.pipe(
debounceTime(400),
distinctUntilChanged(),
switchMap((searchTerm: string) => {
const sortedBy: ChallengePlatformSort = 'name';
const platformQuery: ChallengePlatformSearchQuery = {
searchTerms: searchTerm,
sort: sortedBy,
};
return this.challengePlatformService.listChallengePlatforms(
platformQuery
);
switchMap((searchQuery: ChallengePlatformSearchQuery) => {
return this.challengePlatformService.listChallengePlatforms({
...searchQuery,
...newQuery,
});
}),
map((page) =>
page.challengePlatforms.map((platform) => ({
Expand All @@ -152,4 +159,24 @@ export class ChallengeSearchDataService {
)
);
}

setSearchQuery(dropdown: ChallengeSearchDropdown, searchQuery = {}) {
const setQueryMethods = {
inputDataTypes: () => this.setEdamConceptSearchQuery(searchQuery),
organizations: () => this.setOriganizationSearchQuery(searchQuery),
platforms: () => this.setPlatformSearchQuery(searchQuery),
};

return setQueryMethods[dropdown]();
}

fetchData(dropdown: ChallengeSearchDropdown, searchQuery = {}) {
const fetchDataMethods = {
inputDataTypes: () => this.getEdamConcepts(searchQuery),
organizations: () => this.getOriganizations(searchQuery),
platforms: () => this.getPlatforms(searchQuery),
};

return fetchDataMethods[dropdown]();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type ChallengeSearchDropdown =
| 'inputDataTypes'
| 'organizations'
| 'platforms';
Original file line number Diff line number Diff line change
Expand Up @@ -74,50 +74,53 @@ <h2>Challenges</h2>
</p-panel>
<p-divider></p-divider>
<p-panel
header="{{ platformsFilter.label }}"
header="{{ dropdownFilters['platforms'].label }}"
[toggleable]="true"
[collapsed]="platformsFilter.collapsed"
[collapsed]="dropdownFilters['platforms'].collapsed"
>
<openchallenges-search-dropdown-filter
[options]="platformsFilter.options"
[options]="dropdownFilters['platforms'].options"
[selectedOptions]="selectedPlatforms"
placeholder="{{ platformsFilter.label.toLowerCase() + '(s)' }} "
[showAvatar]="platformsFilter.showAvatar"
placeholder="{{ dropdownFilters['platforms'].label.toLowerCase() + '(s)' }} "
[showAvatar]="dropdownFilters['platforms'].showAvatar"
[filterByApiClient]="true"
(selectionChange)="onParamChange({ platforms: $event })"
(searchChange)="onSearchChange('platforms', $event)"
(lazyLoad)="onLazyLoad('platforms', $event)"
/>
</p-panel>
<p-divider></p-divider>
<p-panel
header="{{ inputDataTypesFilter.label }}"
header="{{ dropdownFilters['inputDataTypes'].label }}"
[toggleable]="true"
[collapsed]="inputDataTypesFilter.collapsed"
[collapsed]="dropdownFilters['inputDataTypes'].collapsed"
>
<openchallenges-search-dropdown-filter
[options]="inputDataTypesFilter.options"
[options]="dropdownFilters['inputDataTypes'].options"
[selectedOptions]="selectedInputDataTypes"
placeholder="{{ inputDataTypesFilter.label.toLowerCase() + '(s)' }} "
[showAvatar]="inputDataTypesFilter.showAvatar"
placeholder="{{ dropdownFilters['inputDataTypes'].label.toLowerCase() + '(s)' }} "
[showAvatar]="dropdownFilters['inputDataTypes'].showAvatar"
[filterByApiClient]="true"
(selectionChange)="onParamChange({ inputDataTypes: $event })"
(searchChange)="onSearchChange('inputDataTypes', $event)"
(lazyLoad)="onLazyLoad('inputDataTypes', $event)"
/>
</p-panel>
<p-divider></p-divider>
<p-panel
header="{{ organizationsFilter.label }}"
header="{{ dropdownFilters['organizations'].label }}"
[toggleable]="true"
[collapsed]="organizationsFilter.collapsed"
[collapsed]="dropdownFilters['organizations'].collapsed"
>
<openchallenges-search-dropdown-filter
[options]="organizationsFilter.options"
[options]="dropdownFilters['organizations'].options"
[selectedOptions]="selectedOrgs"
placeholder="{{ organizationsFilter.label.toLowerCase() + '(s)' }} "
[showAvatar]="organizationsFilter.showAvatar"
placeholder="{{ dropdownFilters['organizations'].label.toLowerCase() + '(s)' }} "
[showAvatar]="dropdownFilters['organizations'].showAvatar"
[filterByApiClient]="true"
(selectionChange)="onParamChange({ organizations: $event })"
(searchChange)="onSearchChange('organizations', $event)"
(lazyLoad)="onLazyLoad('organizations', $event)"
/>
</p-panel>
<p-divider></p-divider>
Expand Down
Loading

0 comments on commit 8322a8f

Please sign in to comment.