libs/ngx-pfe/services/pfe-navigation-service/navigation.service.ts
The PfeNavigationService is responsible to determine the next page to be displayed depending on the configuration and the current values in the state.
Properties |
|
Methods |
|
constructor(router: Router, pfeStateService: PfeStateService, pfeConfigService: PfeConfigurationService, pfeServiceActivatorService: PfeServiceActivatorService, pfeNavigationUtilService: PfeNavigationUtilService, pfeActionsService: PfeActionsService, logger: NgxLoggerService)
|
||||||||||||||||||||||||
Parameters :
|
Public determineUpdateStateConfig | ||||||||||||
determineUpdateStateConfig(key: string, pageNavConfig: NavOptionConfig[] | undefined)
|
||||||||||||
This method determine which is the navigation config of a give key
Parameters :
Returns :
NavOptionConfig | undefined
|
Public Async getErrorPage | ||||||
getErrorPage(errorResponse?: HttpErrorResponse)
|
||||||
Parameters :
Returns :
Promise<string | undefined>
|
Public Async getFirstPage | ||||||
getFirstPage(disableErrorPageNavigation?: boolean)
|
||||||
Parameters :
Returns :
Promise<string | undefined>
|
Public Async getFirstPageOption | ||||||
getFirstPageOption(disableErrorPageNavigation?: boolean)
|
||||||
Parameters :
Returns :
Promise<NavOptionConfig | undefined>
|
Public Async getFirstPageOrErrorPageOption |
getFirstPageOrErrorPageOption()
|
Tries to get the first page. If that fails, the error page is returned. This is mostly useful during startup of the application. Instead of the first page, the error page is simply displayed.
Returns :
Promise<NavOptionConfig | undefined>
|
Public Async getNextPage | ||||||||
getNextPage(pageNavConfig: PageNavigationConfiguration | FirstPageConfiguration)
|
||||||||
Determine the next page to be displayed.
Parameters :
Returns :
Promise<string | undefined>
The next page. |
Public Async getNextPageOption | ||||||
getNextPageOption(pageNavConfig: PageNavigationConfiguration | FirstPageConfiguration)
|
||||||
Parameters :
Returns :
Promise<NavOptionConfig | undefined>
|
Public Async getPreviousPage | ||||||
getPreviousPage(pageNavConfig: PageNavigationConfiguration)
|
||||||
Parameters :
Returns :
Promise<string | undefined>
|
Public getPreviousPageOnHistory | ||||||
getPreviousPageOnHistory(omitFromHistory)
|
||||||
Parameters :
Returns :
string | undefined
|
Public Async getPreviousPageOption | ||||||
getPreviousPageOption(pageNavConfig: PageNavigationConfiguration)
|
||||||
Parameters :
Returns :
Promise<NavOptionConfig | undefined>
|
Public Async navigate | ||||||||||||||||
navigate(nextPage: string | undefined, doNotActuallyNavigate?: boolean, navigationType?: NavigationType)
|
||||||||||||||||
Navigates directly to a specific page. Does not execute the onPageLeave service activators. Does not execute state navigation updates but do not trigger the actual navigation. Usecase: The actual navigation is done by some other mechanism. For example the browser back button
Parameters :
Returns :
Promise<PageConfig | undefined>
|
Public navigateBack | ||||||
navigateBack(doNotActuallyNavigate?: boolean)
|
||||||
Navigate to the previous page in the flow. PfeNavigationService.navigate
Parameters :
Returns :
Promise<PageConfig | undefined>
|
Public navigateNext | ||||||
navigateNext(doNotActuallyNavigate?: boolean)
|
||||||
Navigate to the next page. Also triggers all actions/service activators, etc... PfeNavigationService.navigate
Parameters :
Returns :
Promise<PageConfig | undefined>
|
Public Async navigateNextWithConfig | |||||||||
navigateNextWithConfig(currentPageNavCfg: PageNavigationConfiguration | undefined, doNotActuallyNavigate?: boolean)
|
|||||||||
Parameters :
Returns :
Promise<PageConfig | undefined>
|
Public Async navigateToErrorPage | ||||||
navigateToErrorPage(errorResponse?: HttpErrorResponse)
|
||||||
Parameters :
Returns :
unknown
|
Public Async navigateToPageId | ||||||||||||||||
navigateToPageId(pageId: string, triggerLeaveActions?: boolean, doNotActuallyNavigate?: boolean)
|
||||||||||||||||
Navigate to a given pageId
Parameters :
Returns :
Promise<PageConfig | undefined>
|
Public popPageFromHistory | ||||||
popPageFromHistory(omitFromHistory: boolean | undefined)
|
||||||
Parameters :
Returns :
any
|
Public pushPageToHistory | ||||||
pushPageToHistory(pageID: string)
|
||||||
Parameters :
Returns :
void
|
Public Async updateStateOnBackend | |||||||||
Please switch to updateStoredState() and handle the storage of the last visited page on the calling side. | |||||||||
updateStateOnBackend(currentPageName: string, navConfig: PageNavigationConfiguration)
|
|||||||||
Parameters :
Returns :
any
|
Public Async updateStoredState | ||||||
updateStoredState(navConfig: PageNavigationConfiguration)
|
||||||
Updates the state on a remote backend or the session storage. Checks if the automatic state storage is still active and doesn't do anything if it is shutdown.
Parameters :
Returns :
any
|
Public currentPageId$ |
Type : BehaviorSubject<string | undefined>
|
Default value : this.pfeNavigationUtilService.currentPageId$
|
import { NgxLoggerService } from '@allianz/ngx-logger';
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ChildActivationEnd, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
import { FirstPageConfiguration, NavOptionConfig, NavigationType, PageNavigationConfiguration } from '../../models/navigation-config.model';
import { NgxPfeConfig, PageConfig } from '../../models/ngx-pfe-page-config.model';
import { PfeUserInputState } from '../../models/pfe-state/user-input-state.model';
import { NavServiceActivatorConfigInternally } from '../../pfe-service-activator/service/service-activator.model';
import { PfeServiceActivatorService } from '../../pfe-service-activator/service/service-activator.service';
import { clone } from '../../util/clone';
import { PfeConfigurationService } from '../pfe-config-service/config-service.service';
import { BYPASS_PFE_ROUTING } from '../pfe-routing/route-generator-guards.definition';
import { getPageIDFromRouteSnapshot } from '../pfe-routing/utils/page-id-from-route-snapshot';
import { PFE_HISTORY_KEY, PFE_VISITED_KEY } from '../pfe-state-service/pfe-state-keys';
import { PfeStateService } from '../pfe-state-service/state.service';
import { PfeActionsService } from './../../pfe-actions/pfe-actions.service';
import { LAST_VISITED_PAGE_STATE_KEY, PfeNavigationUtilService } from './navigation-util.service';
/**
* The PfeNavigationService is responsible to determine the next page to be displayed
* depending on the configuration and the current values in the state.
*
* @export
*/
@Injectable()
export class PfeNavigationService {
private readonly PREVENT_REPEATED_NAVIGATION_DURATION = 300;
/**
* Set true, while throttling navigation events
*/
private throttledActive = false;
constructor(
private router: Router,
private pfeStateService: PfeStateService,
private pfeConfigService: PfeConfigurationService,
private pfeServiceActivatorService: PfeServiceActivatorService,
private pfeNavigationUtilService: PfeNavigationUtilService,
private pfeActionsService: PfeActionsService,
private logger: NgxLoggerService
) {
// Even though router events do not differentiate between primary and secondary outlets
// it is possible to subscribe to the ChildActivationEnd events.
// These contain the ActivatedRouteSnapshot, which allows to filter out all non-pfe routes.
this.router.events
.pipe(
filter((event) => event instanceof ChildActivationEnd),
map((event) => {
const childEvent = event as ChildActivationEnd;
return getPageIDFromRouteSnapshot(childEvent.snapshot);
}),
filter((pageID) => pageID != null),
distinctUntilChanged()
)
.subscribe((pageID) => {
(async () => {
const pageNavConfig: PageNavigationConfiguration | undefined = await this.pfeConfigService.getPageNavigationConfiguration(pageID);
if (pageNavConfig && !this.getSkipLocationChangeSetting(pageNavConfig) && pageID) {
if (!history?.state?.[BYPASS_PFE_ROUTING]) {
this.pushPageToHistory(pageID);
}
}
})();
this.pfeNavigationUtilService.currentPageId$.next(pageID);
});
}
/**
* Set to true, when a navigation or network call (service activator call) is currently running.
*/
public navigationInProgress$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public currentPageId$: BehaviorSubject<string | undefined> = this.pfeNavigationUtilService.currentPageId$;
/**
* Determines if the current page is considered valid.
* If it is, navigation is allowed. This not only affects the next/back button
* but also the browser navigation.
*/
public pageStatus$ = new BehaviorSubject<boolean>(false);
/**
* Will be set to true, when the state handling has been shutdown within a flow.
*/
// TODO: Move to state service:
private stateStorageIsShutdown = false;
/**
* Navigate to the next page.
* Also triggers all actions/service activators, etc...
* {@link PfeNavigationService.navigate}
*/
public navigateNext(doNotActuallyNavigate?: boolean): Promise<PageConfig | undefined> {
if (this.throttledActive) {
return Promise.resolve(undefined);
}
this.throttledActive = true;
setTimeout(() => {
this.throttledActive = false;
}, this.PREVENT_REPEATED_NAVIGATION_DURATION);
return this._navigateNext(doNotActuallyNavigate);
}
private async _navigateNext(doNotActuallyNavigate?: boolean): Promise<PageConfig | undefined> {
const currentPageNavCfg: PageNavigationConfiguration | undefined = await this.pfeConfigService.getPageNavigationConfiguration(
this.currentPageId$.value
);
return this.navigateNextWithConfig(currentPageNavCfg, doNotActuallyNavigate);
}
public async navigateNextWithConfig(
currentPageNavCfg: PageNavigationConfiguration | undefined,
doNotActuallyNavigate?: boolean
): Promise<PageConfig | undefined> {
if (!currentPageNavCfg?.nextOptionList) {
return;
}
this.navigationInProgress$.next(true);
try {
const actionsResult = await this.navigateNextActions(currentPageNavCfg);
if (!actionsResult) {
this.navigationInProgress$.next(false);
return;
}
} catch (e) {
this.navigationInProgress$.next(false);
// Error handling is done in httpErrorHandler
return;
}
try {
let nextPageId: string | undefined;
const nextPageOption: NavOptionConfig | undefined = await this.evaluateNavOptions(currentPageNavCfg.nextOptionList);
if (this.pfeNavigationUtilService.navigateExternal(nextPageOption)) {
return;
}
if (nextPageOption?.nextPageId) {
nextPageId = nextPageOption?.nextPageId;
const navToUpdate: NavOptionConfig | undefined = this.determineUpdateStateConfig(nextPageId, currentPageNavCfg.nextOptionList);
/** @todo DEPRECATED, will be remove after the implementation of PFE Actions */
if (navToUpdate?.updateStateValues) {
this.pfeStateService.updateStateValues(navToUpdate.updateStateValues);
}
}
if (nextPageId) {
let pageIdsToRemove = [];
pageIdsToRemove = currentPageNavCfg.nextOptionList
.filter(
(page) =>
page.nextPageId &&
page.nextPageId !== nextPageId &&
Object.prototype.hasOwnProperty.call(this.pfeStateService._staleState.pages, page.nextPageId)
)
.map((page) => page.nextPageId);
this.pfeStateService.analyzePageForStale({
pageId: nextPageId,
removePageFromState: pageIdsToRemove as string[],
});
return await this.navigate(nextPageId, doNotActuallyNavigate, NavigationType.FORWARD);
} else {
this.navigationInProgress$.next(false);
this.logger.warnToServer('Next navigation options config did not lead to a result: ', currentPageNavCfg);
}
} catch (e) {
this.navigationInProgress$.next(false);
throw e;
}
}
/**
* Execute all the actions related with the "onPageLeave*":
* - Execute the onPageLeaveActions
* - Execute the onPageLeaveServiceActivators
* - Check for a new error key.
* @throws Error if an error happens on the service activators or the actions.
* Also rejects if the "errorMessage" is present
* @param currentPageNavCfg The page configuration.
* @returns A promise that signals if all actions returned a positive status.
*/
private async navigateNextActions(currentPageNavCfg: PageNavigationConfiguration | undefined): Promise<boolean> {
if (!currentPageNavCfg) {
return true;
}
// clean error message data on navigation so message is not still displayed
this.pfeStateService.storeValue(await this.pfeConfigService.getErrorMessageKey(), undefined);
if (currentPageNavCfg.onPageLeaveActions) {
const actionsResult = await this.pfeActionsService.executeActions(currentPageNavCfg.onPageLeaveActions, (notBusy) =>
this.navigationInProgress$.next(!notBusy)
);
if (!actionsResult) {
return false;
}
}
/** @todo DEPRECATED, will be remove after the implementation of PFE Actions */
if (currentPageNavCfg.onPageLeaveServiceActivators) {
await this.pfeServiceActivatorService.spreadWithGlobalServiceActivatorConfig(currentPageNavCfg.onPageLeaveServiceActivators);
await this.pfeServiceActivatorService.handleServiceActivators(
currentPageNavCfg.onPageLeaveServiceActivators,
currentPageNavCfg.executeServiceActivatorsSync
);
}
const errorMessageKey = await this.pfeConfigService.getErrorMessageKey();
const errorMessage = this.pfeStateService.getValue(errorMessageKey);
if (errorMessage) {
return Promise.reject(undefined);
}
return true;
}
/**
* Navigate to the previous page in the flow.
* {@link PfeNavigationService.navigate}
*/
public navigateBack(doNotActuallyNavigate?: boolean): Promise<PageConfig | undefined> {
if (this.throttledActive) {
return Promise.resolve(undefined);
}
this.throttledActive = true;
setTimeout(() => {
this.throttledActive = false;
}, this.PREVENT_REPEATED_NAVIGATION_DURATION);
return this._navigateBack(doNotActuallyNavigate);
}
private async _navigateBack(doNotActuallyNavigate?: boolean): Promise<PageConfig | undefined> {
const currentPage = this.currentPageId$.value;
const currentPageNavCfg: PageNavigationConfiguration | undefined =
await this.pfeConfigService.getPageNavigationConfiguration(currentPage);
let nextPageId!: string;
if (currentPageNavCfg) {
this.navigationInProgress$.next(true);
try {
const actionsResult = await this.navigateBackActions(currentPageNavCfg);
if (!actionsResult) {
this.navigationInProgress$.next(false);
return undefined;
}
} catch (error) {
this.navigationInProgress$.next(false);
// Error handling is done in httpErrorHandler for service activators and in the actions for actions.
return undefined;
}
if (currentPageNavCfg.backOptionList) {
const backPageOption: NavOptionConfig | undefined = await this.evaluateNavOptions(currentPageNavCfg.backOptionList);
if (this.pfeNavigationUtilService.navigateExternal(backPageOption)) {
return undefined;
}
if (backPageOption?.nextPageId) {
nextPageId = backPageOption?.nextPageId;
}
const navToUpdate: NavOptionConfig | undefined = this.determineUpdateStateConfig(nextPageId, currentPageNavCfg.backOptionList);
if (navToUpdate?.updateStateValues) {
this.pfeStateService.updateStateValues(navToUpdate.updateStateValues);
}
}
}
// There was no backOptionList or no result. What we're gonna do right here is go back, way back, back into time
// Also: If there is an empty backOptionList configured, no backwards navigation is done and the fallback is
// determined with the backOptionListFallBackToHistory flag
if (!nextPageId) {
if (
!currentPageNavCfg?.backOptionList ||
(currentPageNavCfg?.backOptionList?.length !== 0 &&
(await this.pfeConfigService.getAppConfiguration())?.pfeConfig?.backOptionListFallBackToHistory)
) {
const pageIdFromHistory = this.getPreviousPageOnHistory(currentPageNavCfg?.omitFromHistory);
if (pageIdFromHistory) {
nextPageId = pageIdFromHistory;
}
}
}
if (nextPageId) {
// start staleState analysis for back button trigger
if (currentPage) {
this.pfeStateService.analyzePageForStale({
pageId: nextPageId,
removePageFromState: [currentPage],
});
}
return await this.navigate(nextPageId, doNotActuallyNavigate, NavigationType.BACKWARD);
} else {
this.navigationInProgress$.next(false);
this.logger.warnToServer('Back navigation options config did not lead to a result: ', currentPageNavCfg);
}
}
/**
* Execute all the actions related with the "onPageLeaveBackwardsActions":
* - Execute the onPageLeaveBackwardsActions
* @throws Error if an error happens on the actions.
* @param currentPageNavCfg The page configuration.
* @returns A promise that signals if all actions returned a positive status.
*/
private async navigateBackActions(currentPageNavCfg: PageNavigationConfiguration): Promise<boolean> {
// clean error message data on navigation so message is not still displayed
this.pfeStateService.storeValue(await this.pfeConfigService.getErrorMessageKey(), undefined);
if (currentPageNavCfg.onPageLeaveBackwardsActions) {
const actionsResult = await this.pfeActionsService.executeActions(currentPageNavCfg.onPageLeaveBackwardsActions);
return actionsResult;
}
return true;
}
/**
* Navigates directly to a specific page.
* Does not execute the onPageLeave service activators.
* Does not execute state navigation updates
*
* @param nextPage The id of the page to navigate to
* @param doNotActuallyNavigate Do everything that is related to a navigation,
* but do not trigger the actual navigation. Usecase: The actual navigation is done by some other
* mechanism. For example the browser back button
*/
public async navigate(
nextPage: string | undefined,
doNotActuallyNavigate?: boolean,
navigationType?: NavigationType
): Promise<PageConfig | undefined> {
if (!nextPage) {
this.logger.errorToServer('Navigation was triggered without a pageID. Navigating nowhere...');
return;
}
if (nextPage !== this.currentPageId$.value) {
const nextPageConfig = await this.pfeConfigService.getPageConfiguration(nextPage);
const nextPageConfigNavConfig: PageNavigationConfiguration | undefined =
await this.pfeConfigService.getPageNavigationConfiguration(nextPage);
if (nextPageConfig) {
if (!doNotActuallyNavigate) {
await this.pfeNavigationUtilService.navigateRelative(nextPage, {
skipLocationChange: this.getSkipLocationChangeSetting(nextPageConfigNavConfig),
state: { navigationType },
});
}
} else {
this.logger.errorToServer(`No configuration for page: ${nextPage}`);
await this.navigateToErrorPage();
}
return nextPageConfig;
}
}
/**
* Navigate to a given pageId
* @param pageId The page id
* @param triggerLeaveActions If should trigger the page leave actions
*/
public async navigateToPageId(
pageId: string,
triggerLeaveActions?: boolean,
doNotActuallyNavigate?: boolean
): Promise<PageConfig | undefined> {
if (triggerLeaveActions) {
const currentPageNavCfg: PageNavigationConfiguration | undefined = await this.pfeConfigService.getPageNavigationConfiguration(
this.currentPageId$.value
);
const actionsResult = await this.navigateNextActions(currentPageNavCfg);
if (!actionsResult) {
return undefined;
}
}
return await this.navigate(pageId, doNotActuallyNavigate, NavigationType.FORWARD);
}
private getSkipLocationChangeSetting(pageNavConfig: PageNavigationConfiguration | undefined): boolean {
let skipLocationChangeSetting = false;
if (pageNavConfig && pageNavConfig.omitFromHistory === true) {
skipLocationChangeSetting = true;
}
return skipLocationChangeSetting;
}
/**
* Updates the state on a remote backend or the session storage.
* Checks if the automatic state storage is still active and doesn't do anything if it is shutdown.
*/
public async updateStoredState(navConfig: PageNavigationConfiguration) {
if (this.stateStorageIsShutdown || (navConfig && navConfig.shutdownStateStorage)) {
this.stateStorageIsShutdown = true;
return;
}
await this.pfeStateService.storeState();
}
/**
* @deprecated Please switch to updateStoredState() and handle the storage of the last visited page on the calling side.
*/
public async updateStateOnBackend(currentPageName: string, navConfig: PageNavigationConfiguration) {
if (!navConfig || (navConfig && !navConfig.omitFromHistory)) {
this.pfeStateService.storeValue(LAST_VISITED_PAGE_STATE_KEY, currentPageName);
}
this.updateStoredState(navConfig);
}
public pushPageToHistory(pageID: string) {
let history = this.pfeStateService.getValue(PFE_HISTORY_KEY) as string[],
visitedPages = this.pfeStateService.getValue(PFE_VISITED_KEY) as string[];
if (!history) {
history = new Array<string>();
}
if (!visitedPages) {
visitedPages = new Array<string>();
}
// TODO: Check if the current top page is the same, only add if not.
// the page is not added if it is already there
if (visitedPages.indexOf(pageID) === -1) {
const lastVisited = visitedPages[visitedPages.length - 1],
historyLast = history[history.length - 1];
// if it the previous page is the same adding it at the end
// if not adding it to after history last one index
if (lastVisited === historyLast) {
visitedPages.push(pageID);
} else {
const lastPageIndex = visitedPages.indexOf(historyLast);
visitedPages.splice(lastPageIndex + 1, 0, pageID);
}
}
history.push(pageID);
this.pfeStateService.storeValue(PFE_HISTORY_KEY, history);
this.pfeStateService.storeValue(PFE_VISITED_KEY, visitedPages);
}
public popPageFromHistory(omitFromHistory: boolean | undefined) {
const history: string[] = this.pfeStateService.getValue(PFE_HISTORY_KEY) as string[];
if (history && history.length >= 1) {
// If the current page was not added to the history, we don't need to skip it when we're going back:
const goBackNumber = omitFromHistory === true ? 1 : 2;
const page = history.splice(history.length - goBackNumber, goBackNumber)[0];
this.pfeStateService.storeValue(PFE_HISTORY_KEY, history);
return page;
}
}
public getPreviousPageOnHistory(omitFromHistory = false): string | undefined {
const history: string[] = this.pfeStateService.getValue(PFE_HISTORY_KEY) as string[];
if (!history || history.length === 0) {
return;
}
// if the current page is the only one in the history, then there is no known previous page
const currentPageIsOnlyHistoryEntry = history.length === 1 && !omitFromHistory;
if (currentPageIsOnlyHistoryEntry) {
return;
}
// If the current page was not added to the history, we don't need to skip it when we're going back:
const goBackNumber = omitFromHistory ? 1 : 2;
return history[history.length - goBackNumber];
}
/**
* Determine the next page to be displayed.
*
* @param pageNavConfig It can be either a pageNavConfig or a first page configuration.
* @returns The next page.
* @throws Exception, if a sync service activator call fails.
*/
public async getNextPage(pageNavConfig: PageNavigationConfiguration | FirstPageConfiguration): Promise<string | undefined> {
return (await this.evaluateNavOptions(pageNavConfig.nextOptionList))?.nextPageId;
}
public async getNextPageOption(
pageNavConfig: PageNavigationConfiguration | FirstPageConfiguration
): Promise<NavOptionConfig | undefined> {
return this.evaluateNavOptions(pageNavConfig.nextOptionList);
}
public async getPreviousPage(pageNavConfig: PageNavigationConfiguration): Promise<string | undefined> {
return (await this.evaluateNavOptions(pageNavConfig.backOptionList))?.nextPageId;
}
public async getPreviousPageOption(pageNavConfig: PageNavigationConfiguration): Promise<NavOptionConfig | undefined> {
return this.evaluateNavOptions(pageNavConfig.backOptionList);
}
/**
* Return the following page to be displayed. This is independent of next or back.
*
* @param pageNavConfig The page navigation configuration.
* @returns The ID of the next page to be displayed. Undefined, if no next page could be determined.
*/
private async evaluateNavOptions(pageNavConfig: NavOptionConfig[] | undefined): Promise<NavOptionConfig | undefined> {
if (pageNavConfig) {
const state: PfeUserInputState = this.pfeStateService.getFullState();
const navOptionConfig = this.pfeNavigationUtilService.determineNextPage(pageNavConfig, state);
if (navOptionConfig) {
if (navOptionConfig?.metaNavigation?.metaNavId) {
return { ...navOptionConfig, nextPageId: await this.getFirstPage() };
} else {
return navOptionConfig;
}
}
}
}
// Forward some calls to the util service. This is done to keep the public API consistent.
public async navigateToErrorPage(errorResponse?: HttpErrorResponse) {
return this.pfeNavigationUtilService.navigateToGlobalErrorPage(errorResponse);
}
/**
* Tries to get the first page. If that fails, the error page is returned.
* This is mostly useful during startup of the application. Instead of the first page,
* the error page is simply displayed.
*/
public async getFirstPageOrErrorPageOption(): Promise<NavOptionConfig | undefined> {
let firstPage!: NavOptionConfig | undefined;
let error;
try {
firstPage = await this.getFirstPageOption(true);
} catch (e) {
error = e;
}
if (!firstPage) {
return this.isHttpErrorResponse(error)
? await this.pfeNavigationUtilService.getGlobalErrorPageOption(error)
: await this.pfeNavigationUtilService.getGlobalErrorPageOption();
}
return firstPage;
}
public async getFirstPage(disableErrorPageNavigation?: boolean): Promise<string | undefined> {
return (await this.getFirstPageOption(disableErrorPageNavigation))?.nextPageId;
}
public async getFirstPageOption(disableErrorPageNavigation?: boolean): Promise<NavOptionConfig | undefined> {
const navConfiguration = ((await this.pfeConfigService.getConfig()) as NgxPfeConfig).navConfiguration;
const firstPageConfig: string | FirstPageConfiguration | undefined = navConfiguration.firstPage || undefined;
let firstPage: NavOptionConfig | undefined;
if (firstPageConfig) {
if (this.isFirstPageConfig(firstPageConfig)) {
if (firstPageConfig.onEnterActions) {
try {
const actionsResult = await this.pfeActionsService.executeActions(firstPageConfig.onEnterActions);
if (!actionsResult) {
return undefined;
}
} catch (error) {
// Error handling of the service activators is done in showErrorPage
if (disableErrorPageNavigation) {
throw error;
}
}
}
/** @todo DEPRECATED, will be remove after the implementation of PFE Actions */
if (firstPageConfig.onEnterServiceActivators) {
try {
await this.pfeServiceActivatorService.spreadWithGlobalServiceActivatorConfig(firstPageConfig.onEnterServiceActivators);
let firstPageServiceActivators: NavServiceActivatorConfigInternally[];
if (disableErrorPageNavigation) {
// 1. Clone the config, we don't want to pollute it...
firstPageServiceActivators = clone(firstPageConfig.onEnterServiceActivators);
// 2. Set the _firstPageServiceActivator flag to disable the standard error handling
// Error navigation has to be done by the caller
firstPageServiceActivators.forEach((serviceActivator) => {
serviceActivator._disableErrorPageNavigation = true;
});
} else {
firstPageServiceActivators = firstPageConfig.onEnterServiceActivators;
}
await this.pfeServiceActivatorService.handleServiceActivators(firstPageServiceActivators);
} catch (error) {
// Error handling of the service activators is done in showErrorPage
if (disableErrorPageNavigation) {
throw error;
}
}
}
firstPage = await this.getNextPageOption(firstPageConfig);
} else {
firstPage = { nextPageId: firstPageConfig };
}
}
if (!firstPage) {
firstPage = await this.pfeNavigationUtilService.getFirstPageOption();
}
// register the firstPage
if (firstPage?.nextPageId) {
this.pfeStateService.analyzePageForStale({
pageId: firstPage?.nextPageId,
});
}
return firstPage;
}
public async getErrorPage(errorResponse?: HttpErrorResponse): Promise<string | undefined> {
return this.pfeNavigationUtilService.getErrorPage(errorResponse);
}
private isFirstPageConfig(pageConfig: string | FirstPageConfiguration): pageConfig is FirstPageConfiguration {
if (pageConfig && (pageConfig as PageNavigationConfiguration).nextOptionList !== undefined) {
return true;
} else {
return false;
}
}
private isHttpErrorResponse(error: HttpErrorResponse | unknown): error is HttpErrorResponse {
return error instanceof HttpErrorResponse;
}
/**
* This method determine which is the navigation config of a give key
* @param key page to be search
* @param pageNavConfig list with all the possible configs
*/
public determineUpdateStateConfig(key: string, pageNavConfig: NavOptionConfig[] | undefined): NavOptionConfig | undefined {
if (!pageNavConfig || pageNavConfig.length === 0) {
return undefined;
}
return pageNavConfig.find((nav) => nav.nextPageId === key);
}
}