PFE Actions

The PFE Actions are a set of actions that the PFE can execute on certain events, like a user entering or leaving, a page. The actions can then trigger Service Activators, state values updates and more. The benefits is, that everything can be mixed and it is also possible to add custom actions in an app using the PFE.

A custom action can be any function within an app.

Actions can also be triggered directly in the code via the PfeActionsService

1. Triggering Service Activators through an Action

For example, if a service activator configuration looks like this:

{
  "pageId": "examplePage",
  "onPageEnterServiceActivators": [
    {
      "globalConfigID": "exampleServiceActivator"
    }
  ]
}

It will look like this, with the pfeActions:

{
  "pageId": "examplePage",
  "onPageEnterActions": [
    {
      "type": "TRIGGER_SERVICE_ACTIVATORS",
      "serviceActivators": [
        {
          "globalConfigID": "myID"
        }
      ]
    }
  ]
}

2. Updating State Values

An updateStateValues configuration can also be handled with an action.

This configuration:

{
  "pageId": "examplePage",
  "updateStateValues": [
    {
      "key": "$.foo",
      "value": "bar"
    }
  ],
  "pageConfig": {}
}

Would turn into this action:

{
  "pageId": "examplePage",
  "onPageEnterActions": [
    {
      "type": "UPDATE_STATE_VALUES",
      "stateUpdates": [
        {
          "key": "$.foo",
          "value": "bar"
        }
      ]
    }
  ]
}

3. onNavigationStartActions

onNavigationStartActions can use conditions to determine the target page of a navigation. Dependent on this, they can be executed or not.

The necessary information is available with the $._pfe.navigationState.navigatingTo expression in the state.

See also the Order of Execution chapter for more information

{
  "pageId": "navigationActionsPage",
  "onNavigationStartActions": [
    {
      "type": "BLOCKING_ACTION",
      "message": "Action: Shall we continue to the dead end?",
      "conditions": [
        {
          "value1Expression": "$._pfe.navigationState.navigatingTo",
          "operator": "==",
          "value2Expression": "deadEndPage"
        }
      ]
    }
  ]
}

4. onPageEnterActions Navigation Conditions

onPageEnterActions can check against the previous page in their conditions.

{
  "pageId": "onPageEnterActionsPage",
  "onPageEnterActions": [
    {
      "type": "BLOCKING_ACTION",
      "message": "Welcome back from the dead end!",
      "conditions": [
        {
          "value1Expression": "$._pfe.navigationState.navigatingFrom",
          "operator": "==",
          "value2Expression": "deadEndPage"
        }
      ]
    }
  ]
}

4.1. Limitations

This information is not available (undefined) when the previous page is outside the pfe flow.

5. Pre Registered pfeActions

The pfe provides a few default actions:

6. Optional Actions

There are also 2 optional actions that are not registered by default, but can be imported in an app:

7. Implementation of Custom Actions

An action simply triggers a function in the code.

Follow these steps, to add a custom action:

7.1. Create a Configuration Data Model

Every action defines a custom configuration data model.

The data model consists of two parts:

  1. The type of the action
  2. The configuration interface

The type is used for the mapping of the configuration to the actual action.

The configuration data model is also integrated into the json-schema. This makes it possible to validate the configuration and to provide autocomplete for available attributes in the configuration.

The model extends the PfeBaseActionConfig.

servus.model.ts

import { PfeBaseActionConfig } from '@allianz/ngx-pfe';

export const ServusActionType = 'SERVUS';

export interface PfeServusActionConfig extends PfeBaseActionConfig {
  type: typeof ServusActionType;
  /**
   * Comments in the configuration model will also be a part of the json-schema.
   */
  name: string;
}

7.2. Implement an Action Function

An action is simply a function that is triggered by the pfe.

While not required, it is common practice to locate the function within an Angular service, which also makes it possible to access other dependencies.

An action function needs to implement the PfeActionFunction, which looks like this:

export type PfeActionFunction = (actionConfig: PfeBaseActionConfig) => Promise<void | boolean>;

It needs to return a Promise. The pfe will wait for this promise to be resolved. Actions are ran one after each other in the configured order.

The promise itself can either be empty (void) or contain a boolean. If it contains a boolean, the result will be used to determine if the navigation should be continued. If it is false, the navigation is aborted.

A void response is assumed to be true, which will allow the navigation to continue.

The mechanism and behavior of this is similar to Angular route guards. This means, if an onPageLeave stops the navigation and doesn't trigger anything else, the user will just stay on the current page and can try it again. It is also possible to trigger another navigation from within the action.

Actions have one parameter, the actionConfig. It contains the configuration of the action, which follows the defined data model.

servus.service.ts

import { Injectable } from '@angular/core';
import { PfeServusActionConfig } from './servus.model';

@Injectable({
  providedIn: 'root',
})
export class PfeServusService {
  constructor() {}

  public executeServus = async (actionConfig: PfeServusActionConfig): Promise<void> => {
    console.log(`Servus ${actionConfig.name}`);
  };
}

After that, the action needs to be registered with the PfeActionsService. This can happen at any time before the action is triggered. For example it could be done in the app.module constructor.

import { PfeActionsService } from  '@allianz/ngx-pfe';
import { PfeServusService } from  './servus.service';
import { ServusActionType } from  './servus.model';

...
constructor(
    private pfeActionsService:  PfeActionsService,
    private pfeServusService: PfeServusService
) {
    this.pfeActionsService.registerAction(ServusActionType, this.pfeServusService.executeServus)
}

The action can now be used in the navigation.json:

{
  "onPageEnterActions": [
    {
      "type": "SERVUS",
      "name": "Alvaro"
    },
    {
      "type": "SERVUS",
      "name": "Tobi"
    }
  ]
}

Options for Custom Actions

When registering an action it is possible to provide further options for this action, like so:

this.pfeActionsService.registerAction(
  ServusActionType,
  this.pfeServusService.executeServus,
  { notBusyWhileRunning: true }
)

Currently, only one option is available:

  • notBusyWhileRunning: boolean - use notBusyWhileRunning: true to let PFE know that the app should not be marked as busy while this action is running. This means, busy$ in the PfeBusinessService is going to be false for as long as this action is active. Currently this is only supported for onPageLeaveActions and onNavigationStartActions.

8. Global Actions

Its possible to use global actions on a very similar way as the global service activators.

It is recommended to use the the ConfigAggregator, which works like this:

Example of global action on file /actions/servus.json

{
  "actionId": "servus",
  "type": "SERVUS",
  "name": "Alexander"
}

And in the navigation json:

{
  "onPageLeaveActions": [
    {
      "globalConfigId": "servus"
    },
    {
      "globalConfigId": "servus",
      "name": "Hamilton"
    }
  ]
}

8.1. Updating the JsonSchema

The custom actions can also be reflected in the navigation.json json-schema.

To do that, we need to create a new file, and then create the json schema from that ``xxx-navigation.model.ts`

import { PageNavigationConfiguration, PfeNavigationConfiguration } from '@allianz/ngx-pfe/models/navigation-config.model';

export type XXXActionConfig = PfeActionConfig | ServusAction;
export interface XXXNavigationConfiguration extends PfeNavigationConfiguration<XXXActionConfig> {}
export interface XXXPageNavigationConfiguration extends PageNavigationConfiguration<XXXActionConfig> {}

9. How to Intercept the Navigation

TL;DR: Use PFE Actions that return a Promise<boolean> to intercept a navigation.

There are multiple ways to intercept a navigation:

  • PFE Actions
  • Angular Route Guards

Actions can be configured in the navigation, whereas route guards are configured on a page level.

⚠ Careful! ⚠ If an action stops the navigation from within the firstPage mechanism, it will automatically fallback to the error page.


Known Issues: There's some known Angular issues around route guards/pfe action guards, if the navigation was triggered with the browser buttons. Dependent on the user interaction, the navigation history might get corrupted. This is especially the case when the forward browser button is used.

Further information:

The pfe already contains workarounds for the backward navigation and these might be extended at some point to cover more cases.

9.1. Execution Order

When leaving a page the execution order is:

  1. PFE Actions & Service Activators
  2. Route Guards

When entering a page, the execution order is:

  1. Route Guards
  2. PFE Actions & Service Activators

9.2. Configuration of Route Guards

The PFE allows to create custom Angular route guards that can be applied to each page in the configuration.

Route guards can trigger on activation or deactivation of a route.

⚠ Careful! ⚠ The deactivation of a route happens after leave actions like the onPageLeaveAction or onPageLeaveServiceActivator are executed! If these should also be intercepted a pfeAction can be used.

An activation route guard however, runs before the pfe code and can therefore also intercept those actions.

Follow these steps, to integrate custom route guards in an app:

  1. Create an enum with different keys per each guard that should be defined. After that, specify the enum in the page definition.
export enum GUARD_KEYS {
  PFE_EXAMPLE_CAN_MATCH = 'PFE_EXAMPLE_CAN_MATCH',
  PFE_EXAMPLE_CAN_ACTIVATE = 'PFE_EXAMPLE_CAN_ACTIVATE',
}

export interface DemoAppPageConfig extends PageConfig<GUARD_KEYS> {}

This will make it possible to get autocomplete for the guards in the json-schema.

  1. The mapping of the guard keys enum to the actual guard class needs to be set with the PFE_GUARDS_CONFIG Injection Token provided by the PFE.
import { PFE_GUARDS_CONFIG } from '@allianz/ngx-pfe';

providers: [
  {
    provide: PFE_GUARDS_CONFIG,
    useValue: {
      PFE_EXAMPLE_CAN_MATCH: canMatchTestGuard,
      PFE_EXAMPLE_CAN_ACTIVATE: canActivateTestGuard,
    },
  },
];
  1. The guards can now be used in the page config with their specified key. The json-schema provides autocomplete on the possible keys.
{
  "pageId": "pageWithRouteGuard",
  "hideNextButton": true,
  "myPageType": {
    "myConfig": "value"
  },
  "guards": {
    "canActivate": ["PFE_EXAMPLE_CAN_ACTIVATE"],
    "canMatch": ["PFE_EXAMPLE_CAN_MATCH"]
  }
}

results matching ""

    No results matching ""