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
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"
}
]
}
]
}
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"
}
]
}
]
}
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"
}
]
}
]
}
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"
}
]
}
]
}
This information is not available (undefined
) when the previous page is outside the pfe flow.
The pfe provides a few default actions:
UPDATE_STATE_VALUES
: This action provides access to the state updates features as an action. See also State Storage
section in the documentation.TRIGGER_SERVICE_ACTIVATORS
: This action can be used to trigger service activators and works exactly the samePFE_UPDATE_STATE_ON_BACKEND
: Trigger the push of the state towards the backend. This is identical to the to the backend stateService functionalityPFE_REWIND_HISTORY
: Allows it to rewind the history. Documentation of the parameters and examples on the usage can be found herePFE_RESET_STATE
: This action makes it possible to completely reset the statePFE_RELOAD_PAGE
: Makes it possible to reload the whole pageThere are also 2 optional actions that are not registered by default, but can be imported in an app:
PFE_NESTED_ACTIONS
: Allows it to nest actions within other actions, within other actions...PFE_NAVIGATE
: Trigger a navigation to a pageIdAn action simply triggers a function in the code.
Follow these steps, to add a custom action:
Every action defines a custom configuration data model.
The data model consists of two parts:
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;
}
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"
}
]
}
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
.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"
}
]
}
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> {}
TL;DR: Use PFE Actions that return a Promise<boolean>
to intercept a navigation.
There are multiple ways to intercept a navigation:
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.
When leaving a page the execution order is:
When entering a page, the execution order is:
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.
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.
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,
},
},
];
{
"pageId": "pageWithRouteGuard",
"hideNextButton": true,
"myPageType": {
"myConfig": "value"
},
"guards": {
"canActivate": ["PFE_EXAMPLE_CAN_ACTIVATE"],
"canMatch": ["PFE_EXAMPLE_CAN_MATCH"]
}
}