File

libs/ngx-pfe/services/pfe-navigation-service/navigation-util.service.ts

Description

The PfeNavigationUtilService contains navigation util functions

Index

Properties
Methods

Constructor

constructor(router: Router, pfeConfigService: PfeConfigurationService, pfeStateService: PfeStateService, pfeUtilService: PfeUtilService, pfeConditionsService: PfeConditionsService, logger: NgxLoggerService)
Parameters :
Name Type Optional
router Router No
pfeConfigService PfeConfigurationService No
pfeStateService PfeStateService No
pfeUtilService PfeUtilService No
pfeConditionsService PfeConditionsService No
logger NgxLoggerService No

Methods

Public determineErrorPageOption
determineErrorPageOption(errorPageNavigation: NavOptionErrorPageConfig[], errorResponse: HttpErrorResponse | undefined)
Parameters :
Name Type Optional
errorPageNavigation NavOptionErrorPageConfig[] No
errorResponse HttpErrorResponse | undefined No
Returns : any
Public determineNextPage
determineNextPage(pageNavConfig: NavOptionConfig[], state: PfeUserInputState | ErrorCaseConditionsState)
Parameters :
Name Type Optional
pageNavConfig NavOptionConfig[] No
state PfeUserInputState | ErrorCaseConditionsState No
Public Async getErrorPage
getErrorPage(errorResponse?: HttpErrorResponse)

Evaluates the errorPageNavigation if it is available. The state and the errorResponse are available in the conditions.

Parameters :
Name Type Optional
errorResponse HttpErrorResponse Yes
getExternalURLParsed
getExternalURLParsed(nextPage: NavOptionConfig)

Parse the URL and the params provided

Parameters :
Name Type Optional Description
nextPage NavOptionConfig No

The next page

Returns : any
Public Async getFirstPage
getFirstPage()
Returns : Promise<string>
Public Async getFirstPageOption
getFirstPageOption()
Public Async getGlobalErrorPageOption
getGlobalErrorPageOption(errorResponse?: HttpErrorResponse)
Parameters :
Name Type Optional
errorResponse HttpErrorResponse Yes
Public navigateExternal
navigateExternal(navOptionConfig: NavOptionConfig | undefined)

Check if a NavOptionConfig contains an external navigation. If that is the case, navigate

Parameters :
Name Type Optional
navOptionConfig NavOptionConfig | undefined No
Returns : boolean

true if the external navigation was triggered, false if not

Public Async navigateRelative
navigateRelative(pageID: string, extras?: NavigationExtras)
Parameters :
Name Type Optional
pageID string No
extras NavigationExtras Yes
Returns : Promise<boolean>
Public Async navigateToErrorPage
navigateToErrorPage(errorPageOption: NavOptionConfig | undefined)
Parameters :
Name Type Optional
errorPageOption NavOptionConfig | undefined No
Returns : any
Public Async navigateToGlobalErrorPage
navigateToGlobalErrorPage(errorResponse?: HttpErrorResponse)
Parameters :
Name Type Optional
errorResponse HttpErrorResponse Yes
Returns : unknown

Properties

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;
  }
}

results matching ""

    No results matching ""