libs/ngx-pfe/services/pfe-navigation-service/navigation-util.service.ts
The PfeNavigationUtilService contains navigation util functions
Properties |
|
Methods |
|
constructor(router: Router, pfeConfigService: PfeConfigurationService, pfeStateService: PfeStateService, pfeUtilService: PfeUtilService, pfeConditionsService: PfeConditionsService, logger: NgxLoggerService)
|
|||||||||||||||||||||
Parameters :
|
Public determineErrorPageOption | |||||||||
determineErrorPageOption(errorPageNavigation: NavOptionErrorPageConfig[], errorResponse: HttpErrorResponse | undefined)
|
|||||||||
Parameters :
Returns :
any
|
Public determineNextPage | |||||||||
determineNextPage(pageNavConfig: NavOptionConfig[], state: PfeUserInputState | ErrorCaseConditionsState)
|
|||||||||
Parameters :
Returns :
NavOptionConfig | undefined
|
Public Async getErrorPage | ||||||
getErrorPage(errorResponse?: HttpErrorResponse)
|
||||||
Evaluates the errorPageNavigation if it is available. The state and the errorResponse are available in the conditions.
Parameters :
Returns :
Promise<string | undefined>
|
getExternalURLParsed | ||||||||
getExternalURLParsed(nextPage: NavOptionConfig)
|
||||||||
Parse the URL and the params provided
Parameters :
Returns :
any
|
Public Async getFirstPage |
getFirstPage()
|
Returns :
Promise<string>
|
Public Async getFirstPageOption |
getFirstPageOption()
|
Returns :
Promise<NavOptionConfig>
|
Public Async getGlobalErrorPageOption | ||||||
getGlobalErrorPageOption(errorResponse?: HttpErrorResponse)
|
||||||
Parameters :
Returns :
Promise<NavOptionConfig | undefined>
|
Public navigateExternal | ||||||
navigateExternal(navOptionConfig: NavOptionConfig | undefined)
|
||||||
Check if a NavOptionConfig contains an external navigation. If that is the case, navigate
Parameters :
Returns :
boolean
true if the external navigation was triggered, false if not |
Public Async navigateRelative | |||||||||
navigateRelative(pageID: string, extras?: NavigationExtras)
|
|||||||||
Parameters :
Returns :
Promise<boolean>
|
Public Async navigateToErrorPage | ||||||
navigateToErrorPage(errorPageOption: NavOptionConfig | undefined)
|
||||||
Parameters :
Returns :
any
|
Public Async navigateToGlobalErrorPage | ||||||
navigateToGlobalErrorPage(errorResponse?: HttpErrorResponse)
|
||||||
Parameters :
Returns :
unknown
|
Public currentPageId$ |
Default value : new BehaviorSubject<string | undefined>(undefined)
|
import { NgxLoggerService } from '@allianz/ngx-logger';
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { NavigationExtras, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { NavOptionErrorPageConfig } from '../../models/navigation-config-error-page.model';
import { NavOptionConfig, PfeNavigationConfiguration } from '../../models/navigation-config.model';
import { PfeUserInputState } from '../../models/pfe-state/user-input-state.model';
import { ErrorCaseConditionsState } from '../../pfe-service-activator/service/service-activator.model';
import { PfeUtilService, STATE_EXPRESSION, URL_STATE_REGEX } from '../../pfe-util/services/util.service';
import { convertHTTPHeaders } from '../../util/convert-http-response-headers';
import { PfeConfigurationService } from '../pfe-config-service/config-service.service';
import { PfeStateService } from '../pfe-state-service/state.service';
import { AngularNavigationExtrasPFE } from './pfe-navigation-extras';
import { PfeConditionsService } from '../../pfe-conditions/public-api';
export const LAST_VISITED_PAGE_STATE_KEY = 'lastVisitedPage';
// This service is mostly there, to avoid a circular dependency.
/**
* The PfeNavigationUtilService contains navigation util functions
*/
@Injectable()
export class PfeNavigationUtilService {
public currentPageId$ = new BehaviorSubject<string | undefined>(undefined);
constructor(
private router: Router,
private pfeConfigService: PfeConfigurationService,
private pfeStateService: PfeStateService,
private pfeUtilService: PfeUtilService,
private pfeConditionsService: PfeConditionsService,
private logger: NgxLoggerService
) {}
public async navigateToGlobalErrorPage(errorResponse?: HttpErrorResponse) {
const errorPageOption = await this.getGlobalErrorPageOption(errorResponse);
return this.navigateToErrorPage(errorPageOption);
}
public async navigateToErrorPage(errorPageOption: NavOptionConfig | undefined) {
if (!errorPageOption) {
this.logger.errorToServer('Tried to navigate to error page, without error page option configuration');
return;
}
this.logger.warn(`Navigating to errorPage:`, errorPageOption);
if (this.navigateExternal(errorPageOption)) {
return;
}
if (errorPageOption?.nextPageId) {
// The NavigationExtras are used to attach the information that this is a navigation to the error page.
// This prevents the cleanup of the error message in the state that would usually happen.
const navigationExtras = {
state: {
pfe: {
navigatingToErrorPage: true,
},
},
} as AngularNavigationExtrasPFE;
await this.navigateRelative(errorPageOption.nextPageId, navigationExtras);
} else {
this.logger.errorToServer(
'A navigation to the error page was triggered. But there is no error page. Please update the configuration.'
);
}
}
public async navigateRelative(pageID: string, extras?: NavigationExtras): Promise<boolean> {
// Currently, there is no way to trigger a relative navigation from within a service without
// the current activatedRoute.
// This workaround, works, but we should keep our eyes open for a better solution.
// If it doesn't work anymore, we need to get the ActivatedRoute as a parameter for this method.
const urlSegments = this.router.url.split('/');
urlSegments.pop();
urlSegments.push(pageID);
let success;
try {
success = await this.router.navigate(urlSegments, extras);
} catch (error) {
this.logger.errorToServer('Error during navigation', error);
throw error;
}
// navigatePromise.catch((error) => {
// // Navigation failed hard.
// this.navigationError = true;
// });
return success;
}
public async getFirstPage(): Promise<string> {
return (await this.pfeConfigService.getConfig()).navConfiguration.pages[0].pageId;
}
public async getFirstPageOption(): Promise<NavOptionConfig> {
return {
nextPageId: await this.getFirstPage(),
};
}
/**
* Evaluates the errorPageNavigation if it is available. The state and the errorResponse are available
* in the conditions.
*/
public async getErrorPage(errorResponse?: HttpErrorResponse): Promise<string | undefined> {
return (await this.getGlobalErrorPageOption(errorResponse))?.nextPageId;
}
public async getGlobalErrorPageOption(errorResponse?: HttpErrorResponse): Promise<NavOptionConfig | undefined> {
const navConfiguration: PfeNavigationConfiguration = (await this.pfeConfigService.getConfig()).navConfiguration;
if (navConfiguration.errorPageNavigation) {
return this.determineErrorPageOption(navConfiguration.errorPageNavigation, errorResponse);
} else {
return {
nextPageId: navConfiguration.errorPageID,
};
}
}
public determineErrorPageOption(errorPageNavigation: NavOptionErrorPageConfig[], errorResponse: HttpErrorResponse | undefined) {
const state: PfeUserInputState = this.pfeStateService.getFullState();
const nextPageOption = this.determineNextPage(errorPageNavigation, {
state: state,
response: errorResponse,
responseHeaders: convertHTTPHeaders(errorResponse),
});
if (!nextPageOption) {
this.logger.errorToServer('Could not determine error page');
}
return nextPageOption;
}
public determineNextPage(
pageNavConfig: NavOptionConfig[],
state: PfeUserInputState | ErrorCaseConditionsState
): NavOptionConfig | undefined {
for (const page of pageNavConfig) {
let ruleResult: boolean;
if (!page.conditions) {
ruleResult = true;
} else {
ruleResult = this.pfeConditionsService.evaluateConditions(page.conditions, state);
}
if (ruleResult) {
if (page.external) {
return page;
} else {
if (page.nextPageId && page.nextPageId.startsWith(STATE_EXPRESSION)) {
return { ...page, nextPageId: this.pfeStateService.getStateValueByExpression(page.nextPageId) };
}
return page;
}
}
}
return undefined;
}
/**
* Parse the URL and the params provided
* @param nextPage The next page
*/
getExternalURLParsed(nextPage: NavOptionConfig) {
let nextPageId = nextPage.nextPageId;
if (nextPage.externalNavigationPathParams || nextPage.externalPathParamPropagations) {
const params: string[] = [];
if (nextPage.externalNavigationPathParams) {
const state = this.pfeStateService.getFullState();
nextPage.externalNavigationPathParams
.filter((pathParams) =>
pathParams.conditions ? this.pfeConditionsService.evaluateConditions(pathParams.conditions, state) : true
)
.forEach((pathParams) => {
// Get the value
const value = this.getStateValueOrLiteral(pathParams.value);
// If name its provided, we just add as a param
if (pathParams.name) {
params.push(`${pathParams.name}=${value}`);
} else if (pathParams.id) {
// If id its provided, we replace the value
nextPageId = nextPageId?.replace(`{${pathParams.id}}`, value);
}
});
}
if (nextPage.externalPathParamPropagations) {
nextPage.externalPathParamPropagations.forEach((paramName) => {
const paramValue = this.pfeUtilService.getURLParam(paramName);
if (paramValue) {
params.push(`${paramName}=${paramValue}`);
}
});
}
// Join the params with "&"
const stringParams = params.join('&');
if (!stringParams) {
return nextPageId;
} else {
// We can have 3 cases:
// 1. Ends with '?' that means we should just add all the params
// 2. Includes '?' but not end with that, that means we need to concat the custom param with the added ones
// 3. No '?' that means we need to add the params
if (nextPageId?.includes('?') && nextPageId.endsWith('?')) {
return nextPageId + stringParams;
} else if (nextPageId?.includes('?')) {
return nextPageId + '&' + stringParams;
} else {
return nextPageId + '?' + stringParams;
}
}
} else {
const toReplace = nextPageId?.match(URL_STATE_REGEX);
// We iterate over the matched (if any) replacing the matched by their JSONPath Expression
if (toReplace) {
for (const matched of toReplace) {
// The matched is something like "{$.aaa.bbbb}" we transform to $.aaa.bbbb
// to be parsed by translate to JSON path expression
const jsonPathExpression = matched.replace(/[{}]/g, '');
nextPageId = nextPageId?.replace(matched, this.pfeStateService.getStateValueByExpression(jsonPathExpression));
}
}
return nextPageId;
}
}
/**
* Check if a NavOptionConfig contains an external navigation.
* If that is the case, navigate
* @returns {boolean} true if the external navigation was triggered, false if not
*/
public navigateExternal(navOptionConfig: NavOptionConfig | undefined): boolean {
if (navOptionConfig?.external) {
// Navigate to the external URL:
const externalURL = this.getExternalURLParsed(navOptionConfig);
if (externalURL) {
window.location.href = externalURL;
} else {
this.logger.errorToServer('Could not parse external URL from navigation config', navOptionConfig);
}
return true;
}
return false;
}
private getStateValueOrLiteral(value: string) {
return value.startsWith(STATE_EXPRESSION) ? this.pfeStateService.getStateValueByExpression(value) : value;
}
}