Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Port main] Exclude search and browse from Angular SSR (#3709) #3886

Merged
merged 1 commit into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ ssr:
inlineCriticalCss: false
# Path prefixes to enable SSR for. By default these are limited to paths of primary DSpace objects.
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ]
# Whether to enable rendering of Search component on SSR.
# If set to true the component will be included in the HTML returned from the server side rendering.
# If set to false the component will not be included in the HTML returned from the server side rendering.
enableSearchComponent: false,
# Whether to enable rendering of Browse component on SSR.
# If set to true the component will be included in the HTML returned from the server side rendering.
# If set to false the component will not be included in the HTML returned from the server side rendering.
enableBrowseComponent: false,

# The REST API server settings
# NOTE: these settings define which (publicly available) REST API to use. They are usually
Expand Down Expand Up @@ -450,6 +458,12 @@ search:
enabled: false
# List of filters to enable in "Advanced Search" dropdown
filter: [ 'title', 'author', 'subject', 'entityType' ]
#
# Number used to render n UI elements called loading skeletons that act as placeholders.
# These elements indicate that some content will be loaded in their stead.
# Since we don't know how many filters will be loaded before we receive a response from the server we use this parameter for the skeletons count.
# e.g. If we set 5 then 5 loading skeletons will be visualized before the actual filters are retrieved.
defaultFiltersCount: 5


# Notify metrics
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"ng2-nouislider": "^2.0.0",
"ngx-infinite-scroll": "^16.0.0",
"ngx-pagination": "6.0.3",
"ngx-skeleton-loader": "^9.0.0",
"ngx-ui-switch": "^14.1.0",
"nouislider": "^15.7.1",
"orejime": "^2.3.1",
Expand Down
34 changes: 34 additions & 0 deletions src/app/browse-by/browse-by-date/browse-by-date.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { CommonModule } from '@angular/common';
import {
ChangeDetectorRef,
NO_ERRORS_SCHEMA,
PLATFORM_ID,
} from '@angular/core';
import {
ComponentFixture,
fakeAsync,
TestBed,
tick,
waitForAsync,
} from '@angular/core/testing';
import {
Expand All @@ -26,6 +29,7 @@ import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-
import { SortDirection } from '../../core/cache/models/sort-options.model';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { BrowseEntry } from '../../core/shared/browse-entry.model';
import { Community } from '../../core/shared/community.model';
import { Item } from '../../core/shared/item.model';
import { ThemedBrowseByComponent } from '../../shared/browse-by/themed-browse-by.component';
Expand Down Expand Up @@ -123,6 +127,7 @@ describe('BrowseByDateComponent', () => {
{ provide: ChangeDetectorRef, useValue: mockCdRef },
{ provide: Store, useValue: {} },
{ provide: APP_CONFIG, useValue: environment },
{ provide: PLATFORM_ID, useValue: 'browser' },
],
schemas: [NO_ERRORS_SCHEMA],
})
Expand Down Expand Up @@ -172,4 +177,33 @@ describe('BrowseByDateComponent', () => {
//expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear());
expect(comp.startsWithOptions[0]).toEqual(1960);
});

describe('when rendered in SSR', () => {
beforeEach(() => {
comp.platformId = 'server';
spyOn((comp as any).browseService, 'getBrowseItemsFor');
});

it('should not call getBrowseItemsFor on init', (done) => {
comp.ngOnInit();
expect((comp as any).browseService.getBrowseItemsFor).not.toHaveBeenCalled();
comp.loading$.subscribe((res) => {
expect(res).toBeFalsy();
done();
});
});
});

describe('when rendered in CSR', () => {
beforeEach(() => {
comp.platformId = 'browser';
spyOn((comp as any).browseService, 'getBrowseItemsFor').and.returnValue(createSuccessfulRemoteDataObject$(new BrowseEntry()));
});

it('should call getBrowseItemsFor on init', fakeAsync(() => {
comp.ngOnInit();
tick(100);
expect((comp as any).browseService.getBrowseItemsFor).toHaveBeenCalled();
}));
});
});
11 changes: 10 additions & 1 deletion src/app/browse-by/browse-by-date/browse-by-date.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
AsyncPipe,
isPlatformServer,
NgIf,
} from '@angular/common';
import {
ChangeDetectorRef,
Component,
Inject,
OnInit,
PLATFORM_ID,
} from '@angular/core';
import {
ActivatedRoute,
Expand All @@ -17,6 +19,7 @@ import { TranslateModule } from '@ngx-translate/core';
import {
combineLatest as observableCombineLatest,
Observable,
of as observableOf,
} from 'rxjs';
import {
map,
Expand All @@ -28,6 +31,7 @@ import {
APP_CONFIG,
AppConfig,
} from '../../../config/app-config.interface';
import { environment } from '../../../environments/environment';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { BrowseService } from '../../core/browse/browse.service';
import {
Expand Down Expand Up @@ -99,11 +103,16 @@ export class BrowseByDateComponent extends BrowseByMetadataComponent implements
@Inject(APP_CONFIG) public appConfig: AppConfig,
public dsoNameService: DSONameService,
protected cdRef: ChangeDetectorRef,
@Inject(PLATFORM_ID) public platformId: any,
) {
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService);
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService, platformId);
}

ngOnInit(): void {
if (!this.renderOnServerSide && !environment.ssr.enableBrowseComponent && isPlatformServer(this.platformId)) {
this.loading$ = observableOf(false);
return;
}
const sortConfig = new SortOptions('default', SortDirection.ASC);
this.startsWithType = StartsWithType.date;
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<section class="comcol-page-browse-section">
<section class="comcol-page-browse-section" *ngIf="(!ssrRenderingDisabled)">
<div class="browse-by-metadata w-100">
<ds-browse-by *ngIf="(loading$ | async) !== true" class="col-xs-12 w-100"
title="{{'browse.title' | translate:{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
NO_ERRORS_SCHEMA,
PLATFORM_ID,
} from '@angular/core';
import {
ComponentFixture,
fakeAsync,
TestBed,
tick,
waitForAsync,
} from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
Expand Down Expand Up @@ -147,6 +152,7 @@ describe('BrowseByMetadataComponent', () => {
{ provide: ThemeService, useValue: getMockThemeService() },
{ provide: SelectableListService, useValue: {} },
{ provide: HostWindowService, useValue: {} },
{ provide: PLATFORM_ID, useValue: 'browser' },
],
schemas: [NO_ERRORS_SCHEMA],
})
Expand Down Expand Up @@ -259,6 +265,35 @@ describe('BrowseByMetadataComponent', () => {
expect(result.fetchThumbnail).toBeTrue();
});
});

describe('when rendered in SSR', () => {
beforeEach(() => {
comp.ssrRenderingDisabled = true;
spyOn((comp as any).browseService, 'getBrowseEntriesFor').and.returnValue(createSuccessfulRemoteDataObject$(null));
});

it('should not call getBrowseEntriesFor on init', (done) => {
comp.ngOnInit();
expect((comp as any).browseService.getBrowseEntriesFor).not.toHaveBeenCalled();
comp.loading$.subscribe((res) => {
expect(res).toBeFalsy();
done();
});
});
});

describe('when rendered in CSR', () => {
beforeEach(() => {
comp.ssrRenderingDisabled = false;
spyOn((comp as any).browseService, 'getBrowseEntriesFor').and.returnValue(createSuccessfulRemoteDataObject$(new BrowseEntry()));
});

it('should call getBrowseEntriesFor on init', fakeAsync(() => {
comp.ngOnInit();
tick(100);
expect((comp as any).browseService.getBrowseEntriesFor).toHaveBeenCalled();
}));
});
});

export function toRemoteData(objects: any[]): Observable<RemoteData<PaginatedList<any>>> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AsyncPipe,
isPlatformServer,
NgIf,
} from '@angular/common';
import {
Expand All @@ -9,6 +10,7 @@ import {
OnChanges,
OnDestroy,
OnInit,
PLATFORM_ID,
} from '@angular/core';
import {
ActivatedRoute,
Expand All @@ -33,6 +35,7 @@ import {
APP_CONFIG,
AppConfig,
} from '../../../config/app-config.interface';
import { environment } from '../../../environments/environment';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { BrowseService } from '../../core/browse/browse.service';
import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model';
Expand Down Expand Up @@ -114,6 +117,11 @@ export class BrowseByMetadataComponent implements OnInit, OnChanges, OnDestroy {
*/
@Input() displayTitle = true;

/**
* Defines whether to fetch search results during SSR execution
*/
@Input() renderOnServerSide: boolean;

scope$: BehaviorSubject<string> = new BehaviorSubject(undefined);

/**
Expand Down Expand Up @@ -194,6 +202,10 @@ export class BrowseByMetadataComponent implements OnInit, OnChanges, OnDestroy {
* Observable determining if the loading animation needs to be shown
*/
loading$ = observableOf(true);
/**
* Whether this component should be rendered or not in SSR
*/
ssrRenderingDisabled = false;

public constructor(protected route: ActivatedRoute,
protected browseService: BrowseService,
Expand All @@ -202,18 +214,23 @@ export class BrowseByMetadataComponent implements OnInit, OnChanges, OnDestroy {
protected router: Router,
@Inject(APP_CONFIG) public appConfig: AppConfig,
public dsoNameService: DSONameService,
@Inject(PLATFORM_ID) public platformId: any,
) {
this.fetchThumbnails = this.appConfig.browseBy.showThumbnails;
this.paginationConfig = Object.assign(new PaginationComponentOptions(), {
id: BBM_PAGINATION_ID,
currentPage: 1,
pageSize: this.appConfig.browseBy.pageSize,
});
this.ssrRenderingDisabled = !this.renderOnServerSide && !environment.ssr.enableBrowseComponent && isPlatformServer(this.platformId);
}


ngOnInit(): void {

if (this.ssrRenderingDisabled) {
this.loading$ = observableOf(false);
return;
}
const sortConfig = new SortOptions('default', SortDirection.ASC);
this.updatePage(getBrowseSearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig));
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
Expand Down Expand Up @@ -336,7 +353,6 @@ export class BrowseByMetadataComponent implements OnInit, OnChanges, OnDestroy {
this.paginationService.clearPagination(this.paginationConfig.id);
}


}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
ComponentFixture,
fakeAsync,
TestBed,
tick,
waitForAsync,
} from '@angular/core/testing';
import {
Expand All @@ -23,6 +25,7 @@ import { BrowseService } from '../../core/browse/browse.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { ItemDataService } from '../../core/data/item-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { BrowseEntry } from '../../core/shared/browse-entry.model';
import { Community } from '../../core/shared/community.model';
import { Item } from '../../core/shared/item.model';
import { ThemedBrowseByComponent } from '../../shared/browse-by/themed-browse-by.component';
Expand Down Expand Up @@ -81,6 +84,7 @@ describe('BrowseByTitleComponent', () => {

const activatedRouteStub = Object.assign(new ActivatedRouteStub(), {
params: observableOf({}),
queryParams: observableOf({}),
data: observableOf({ metadata: 'title' }),
});

Expand Down Expand Up @@ -127,4 +131,35 @@ describe('BrowseByTitleComponent', () => {
expect(result.payload.page).toEqual(mockItems);
});
});

describe('when rendered in SSR', () => {
beforeEach(() => {
comp.platformId = 'server';
spyOn((comp as any).browseService, 'getBrowseItemsFor');
fixture.detectChanges();
});

it('should not call getBrowseItemsFor on init', (done) => {
comp.ngOnInit();
expect((comp as any).browseService.getBrowseItemsFor).not.toHaveBeenCalled();
comp.loading$.subscribe((res) => {
expect(res).toBeFalsy();
done();
});
});
});

describe('when rendered in CSR', () => {
beforeEach(() => {
comp.platformId = 'browser';
fixture.detectChanges();
spyOn((comp as any).browseService, 'getBrowseItemsFor').and.returnValue(createSuccessfulRemoteDataObject$(new BrowseEntry()));
});

it('should call getBrowseItemsFor on init', fakeAsync(() => {
comp.ngOnInit();
tick(100);
expect((comp as any).browseService.getBrowseItemsFor).toHaveBeenCalled();
}));
});
});
Loading
Loading