diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts index a2cec6b553..3433729902 100644 --- a/src/app/+collection-page/collection-page.component.ts +++ b/src/app/+collection-page/collection-page.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; -import { filter, flatMap, map, take } from 'rxjs/operators'; +import { filter, flatMap, map, switchMap, take } from 'rxjs/operators'; import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model'; import { SearchService } from '../+search-page/search-service/search.service'; import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; @@ -15,7 +15,11 @@ import { Bitstream } from '../core/shared/bitstream.model'; import { Collection } from '../core/shared/collection.model'; import { DSpaceObjectType } from '../core/shared/dspace-object-type.model'; import { Item } from '../core/shared/item.model'; -import { getSucceededRemoteData, toDSpaceObjectListRD } from '../core/shared/operators'; +import { + getSucceededRemoteData, + redirectToPageNotFoundOn404, + toDSpaceObjectListRD +} from '../core/shared/operators'; import { fadeIn, fadeInOut } from '../shared/animations/fade'; import { hasValue, isNotEmpty } from '../shared/empty.util'; @@ -31,22 +35,19 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp fadeInOut ] }) -export class CollectionPageComponent implements OnInit, OnDestroy { +export class CollectionPageComponent implements OnInit { collectionRD$: Observable>; itemRD$: Observable>>; logoRD$: Observable>; paginationConfig: PaginationComponentOptions; sortConfig: SortOptions; - private subs: Subscription[] = []; - private collectionId: string; - private currentPage: number; - private pageSize: number; constructor( private collectionDataService: CollectionDataService, private searchService: SearchService, private metadata: MetadataService, - private route: ActivatedRoute + private route: ActivatedRoute, + private router: Router ) { this.paginationConfig = new PaginationComponentOptions(); this.paginationConfig.id = 'collection-page-pagination'; @@ -57,7 +58,8 @@ export class CollectionPageComponent implements OnInit, OnDestroy { ngOnInit(): void { this.collectionRD$ = this.route.data.pipe( - map((data) => data.collection), + map((data) => data.collection as RemoteData), + redirectToPageNotFoundOn404(this.router), take(1) ); this.logoRD$ = this.collectionRD$.pipe( @@ -65,42 +67,26 @@ export class CollectionPageComponent implements OnInit, OnDestroy { filter((collection: Collection) => hasValue(collection)), flatMap((collection: Collection) => collection.logo) ); - this.subs.push( - this.route.queryParams.subscribe((params) => { - this.metadata.processRemoteData(this.collectionRD$); - const page = +params.page || this.paginationConfig.currentPage; - const pageSize = +params.pageSize || this.paginationConfig.pageSize; - - this.collectionRD$.subscribe((rd: RemoteData) => { - this.collectionId = rd.payload.id; - this.updatePage(page, pageSize); - }); - }) - ); + this.route.queryParams.pipe(take(1)).subscribe((params) => { + this.metadata.processRemoteData(this.collectionRD$); + this.onPaginationChange(params) + }) } - updatePage(currentPage: number, pageSize: number) { - this.itemRD$ = this.searchService.search( - new PaginatedSearchOptions({ - scope: this.collectionId, - pagination: { - currentPage, - pageSize - } as PaginationComponentOptions, - sort: this.sortConfig, - dsoType: DSpaceObjectType.ITEM - })).pipe( - toDSpaceObjectListRD(), + updatePage() { + this.itemRD$ = this.collectionRD$.pipe( getSucceededRemoteData(), - take(1), - ) as Observable>>; - - this.currentPage = currentPage; - this.pageSize = pageSize; - } - - ngOnDestroy(): void { - this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); + map((rd) => rd.payload.id), + switchMap((id: string) => { + return this.searchService.search( + new PaginatedSearchOptions({ + scope: id, + pagination: this.paginationConfig, + sort: this.sortConfig, + dsoType: DSpaceObjectType.ITEM + })).pipe(toDSpaceObjectListRD()) as Observable>>; + }) + ) } isNotEmpty(object: any) { @@ -108,8 +94,10 @@ export class CollectionPageComponent implements OnInit, OnDestroy { } onPaginationChange(event) { - if (this.currentPage !== event.page || this.pageSize !== event.pageSize) { - this.updatePage(event.page, event.pageSize); - } + this.paginationConfig.currentPage = +event.page || this.paginationConfig.currentPage; + this.paginationConfig.pageSize = +event.pageSize || this.paginationConfig.pageSize; + this.sortConfig.direction = event.sortDirection || this.sortConfig.direction; + this.sortConfig.field = event.sortField || this.sortConfig.field; + this.updatePage(); } } diff --git a/src/app/+home-page/home-page.component.html b/src/app/+home-page/home-page.component.html index 6a3e20ca9d..39ba479033 100644 --- a/src/app/+home-page/home-page.component.html +++ b/src/app/+home-page/home-page.component.html @@ -1,5 +1,5 @@
- +
diff --git a/src/app/+my-dspace-page/my-dspace-page.component.html b/src/app/+my-dspace-page/my-dspace-page.component.html index 79fad17b26..744bc4803d 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.html +++ b/src/app/+my-dspace-page/my-dspace-page.component.html @@ -12,9 +12,10 @@ [query]="(searchOptions$ | async)?.query" [scope]="(searchOptions$ | async)?.scope" [currentUrl]="getSearchLink()" - [scopes]="(scopeListRD$ | async)"> + [scopes]="(scopeListRD$ | async)" + [inPlaceSearch]="inPlaceSearch"> - +
+ [ngClass]="{'active': !(isSidebarCollapsed() | async)}" + [inPlaceSearch]="inPlaceSearch">
- +
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.ts b/src/app/+search-page/search-sidebar/search-sidebar.component.ts index 9abcf71dcb..9ee0a74942 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.ts @@ -34,8 +34,14 @@ export class SearchSidebarComponent { */ @Input() viewModeList; + /** + * True when the search component should show results on the current page + */ + @Input() inPlaceSearch; + /** * Emits event when the user clicks a button to open or close the sidebar */ @Output() toggleSidebar = new EventEmitter(); + } diff --git a/src/app/shared/search-form/search-form.component.spec.ts b/src/app/shared/search-form/search-form.component.spec.ts index b164abee1f..a60aeb8054 100644 --- a/src/app/shared/search-form/search-form.component.spec.ts +++ b/src/app/shared/search-form/search-form.component.spec.ts @@ -8,6 +8,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { Community } from '../../core/shared/community.model'; import { TranslateModule } from '@ngx-translate/core'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { SearchService } from '../../+search-page/search-service/search.service'; describe('SearchFormComponent', () => { let comp: SearchFormComponent; @@ -18,6 +19,12 @@ describe('SearchFormComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [FormsModule, RouterTestingModule, TranslateModule.forRoot()], + providers: [ + { + provide: SearchService, + useValue: {} + } + ], declarations: [SearchFormComponent] }).compileComponents(); })); diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index ea96bd8114..410c78c1d5 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -4,6 +4,7 @@ import { Router } from '@angular/router'; import { hasValue, isNotEmpty } from '../empty.util'; import { QueryParamsHandling } from '@angular/router/src/config'; import { MYDSPACE_ROUTE } from '../../+my-dspace-page/my-dspace-page.component'; +import { SearchService } from '../../+search-page/search-service/search.service'; /** * This component renders a simple item page. @@ -26,6 +27,11 @@ export class SearchFormComponent { */ @Input() query: string; + /** + * True when the search component should show results on the current page + */ + @Input() inPlaceSearch; + /** * The currently selected scope object's UUID */ @@ -39,7 +45,7 @@ export class SearchFormComponent { */ @Input() scopes: DSpaceObject[]; - constructor(private router: Router) { + constructor(private router: Router, private searchService: SearchService) { } /** @@ -63,14 +69,10 @@ export class SearchFormComponent { * @param data Updated parameters */ updateSearch(data: any) { - const newUrl = hasValue(this.currentUrl) ? this.currentUrl : '/search'; - let handling: QueryParamsHandling = '' ; - if (this.currentUrl === '/search' || this.currentUrl === MYDSPACE_ROUTE) { - handling = 'merge'; - } - this.router.navigate([newUrl], { + + this.router.navigate(this.getSearchLinkParts(), { queryParams: Object.assign({}, { page: 1 }, data), - queryParamsHandling: handling + queryParamsHandling: 'merge' }); } @@ -81,4 +83,24 @@ export class SearchFormComponent { return isNotEmpty(object); } + + /** + * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true + */ + public getSearchLink(): string { + if (this.inPlaceSearch) { + return './'; + } + return this.searchService.getSearchLink(); + } + + /** + * @returns {string[]} The base path to the search page, or the current page when inPlaceSearch is true, split in separate pieces + */ + public getSearchLinkParts(): string[] { + if (this.inPlaceSearch) { + return []; + } + return this.getSearchLink().split('/'); + } } diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.ts index b011fce6a0..dc355c6409 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.ts +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.ts @@ -17,6 +17,11 @@ import { isEmpty } from '../empty.util'; export class ViewModeSwitchComponent implements OnInit, OnDestroy { @Input() viewModeList: ViewMode[]; + /** + * True when the search component should show results on the current page + */ + @Input() inPlaceSearch; + currentMode: ViewMode = ViewMode.List; viewModeEnum = ViewMode; private sub: Subscription; @@ -35,7 +40,7 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy { } switchViewTo(viewMode: ViewMode) { - this.searchService.setViewMode(viewMode); + this.searchService.setViewMode(viewMode, this.getSearchLinkParts()); } ngOnDestroy() { @@ -47,4 +52,25 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy { isToShow(viewMode: ViewMode) { return this.viewModeList && this.viewModeList.includes(viewMode); } + + /** + * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true + */ + public getSearchLink(): string { + if (this.inPlaceSearch) { + return './'; + } + return this.searchService.getSearchLink(); + } + + /** + * @returns {string[]} The base path to the search page, or the current page when inPlaceSearch is true, split in separate pieces + */ + public getSearchLinkParts(): string[] { + if (this.searchService) { + return []; + } + return this.getSearchLink().split('/'); + } + }