Before we can jump into coding, we need to change our npm registry endpoint and install all dependencies.
Please follow the documentation in ngx-ndbx on how to setup the npm registry configuration.
The latest stable version can be installed like this:
npm install @allianz/ngx-pfe --save
Beta versions are published under the npm next
dist-tag.
The following command displays the latest version per tag:
npm view @allianz/ngx-pfe@* dist-tags
The following command can be used to install the latest beta version:
npm install @allianz/ngx-pfe@next --save
The NGX-PFE provides support for Angular schematics to automatically handle breaking changes during an update. The schematics will run during the update and update the app code accordingly.
Run the following command to update to the latest stable version:
ng update @allianz/ngx-pfe
The changelog can be found here
The NGX-PFE library requires some peer dependencies to function correctly.
NDBX is only required, if the ndbx master page is used. Otherwise it is optional.
A full list of dependencies can be seen in the library package.json
.
NPM will also automatically mention unavailable peer dependencies during the installation. Please ensure, that all of them are installed.
Unfortunately, there is no way in npm to limit peer dependencies to a secondary entry.
To keep the list of the main entry point peer dependencies as short as possible, the ones from the secondary entries were not added there.
As there is no information from npm about the secondary peer dependencies, the easiest way to figure them out is:
In this first part, the general module setup is done. In the second part, the page setup is added.
First, generate a new Angular app with:
npx @angular/cli new --style=scss --routing pfe-example
This guide assumes that the optional ngx-ndbx integration is used, so keep in mind to also install it.
After that, the pfe itself can be installed:
npm install @allianz/ngx-pfe
To use the library, the main module (NgxPfeModule) and configuration data model (NgxPfeModuleConfiguration) has to be imported.
Furthermore, the angular routing has to be setup. The pfe takes over one route and adds all the pages there.
It is recommended to create a separate PfeIntegrationModule
for the NgxPfeModule
setup.
Generate the module with the Angular CLI
npx ng generate @schematics/angular:module --name=PfeIntegration --no-interactive
and replace the content of the pfe-integration-module.module.ts
file with this:
import {
NgxPfeModule,
NgxPfeModuleConfiguration,
NGX_PFE_CONFIGURATION,
NGX_PFE_CONFIG_API_FORMAT,
NGX_PFE_FLOW_CONFIGURATION,
} from '@allianz/ngx-pfe';
import { NgModule } from '@angular/core';
import { pages } from '../pages-mapping';
import { pfeConfig } from '../pfe-config';
// This factory function is responsible to return the module config for the PFE
export function ngxPFEConfigFactory() {
// The factory function can also define dependencies, for example
// embeddedDataService: EmbeddedDataService<undefined, AEMConfiguration>
// This service could then be used to get configurations:
// const applicationId = embeddedDataService.CustomConfiguration.applicationId || environment.applicationId;
const ngxPfeModuleConfiguration: NgxPfeModuleConfiguration = {
/**
* Maps the configuration attributes to a PageDefinition. This defines the available page types that can be used in the flow.
*/
pageMapping: pages,
/**
* The tenant will be used to load the default page flow configuration.
* It can be overwritten by the tenant URL parameter.
*/
tenant: 'tenant',
/**
* The applicationId will be used to load the default page flow configuration.
* It can be overwritten by the applicationId URL parameter.
*/
applicationId: 'app',
// The configuration can also be loaded over HTTP:
// configApiEndpoint: 'http://localhost:4200/assets/config',
// configApiFormat: NGX_PFE_CONFIG_API_FORMAT.STATIC,
production: true,
/**
* The base URL of service activator endpoint.
* It will be composed with the service activator `path` to form a complete URL.
* This complete URL is used when service activators make any HTTP calls.
*/
serviceActivatorEndpoint: 'http://localhost:8080',
};
return ngxPfeModuleConfiguration;
}
@NgModule({
imports: [
NgxPfeModule.forRoot({
provide: NGX_PFE_CONFIGURATION,
/**
* The factory can return a static value or an Angular signal
*/
useFactory: ngxPFEConfigFactory,
}),
],
providers: [
{
provide: NGX_PFE_FLOW_CONFIGURATION,
/**
* The factory can return a static value, a Promise or an Angular signal
*/
useValue: pfeConfig,
},
],
})
export class PfeIntegrationModule {}
The NgxPfeModuleConfiguration
contains the configuration of the NGX-PFE library. In this example, they are provided by the ngxPFEConfigFactory
.
The (NgxPfeModuleConfiguration) describes all the parameters.
In this example the flow configuration is provided directly via the NGX_PFE_FLOW_CONFIGURATION
token.
The Pages & Flow Configuration documentation contains further instructions on how to provide it.
An template for the example application is also provided there.
The simplest routing integration is for the PFE to take over the root routing of the app.
The example AppRoutingModule below shows this integration of the PFE directly in the root.
⚠️ VERY IMPORTANT: The routing setup has to be done after the
NgxPfeModule
import.
It is recommended to generate a separate AppRoutingModule
.
npx ng generate @schematics/angular:module --name=AppRouting --no-interactive
Afterwards, open the app-routing-module.module.ts
and replace the content with this:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
loadChildren: () => import('@allianz/ngx-pfe').then((m) => m.NgxPfeRoutingModule),
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
If the application is integrated into a CMS, like oneMarketing, it is recommended to use the HashOnlyLocationStrategy
provided by the PFE. This custom strategy will keep the url search parameter part intact.
The LocationStrategy
can be added to the providers of the app:
providers: [
{provide: LocationStrategy, useClass: HashOnlyLocationStrategy}
]
Now the app.module.ts
can be updated with the new modules:
import { PfeNdbxMasterPageModule } from '@allianz/ngx-pfe/ndbx';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppRoutingModule } from './app-routing/app-routing.module';
import { AppComponent } from './app.component';
import { PfeIntegrationModule } from './pfe-integration/pfe-integration.module';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
PfeIntegrationModule,
AppRoutingModule,
// By default the PfeNdbxMasterPageModule can be used:
PfeNdbxMasterPageModule,
],
providers: [provideHttpClient(withInterceptorsFromDi())],
bootstrap: [AppComponent],
})
export class AppModule {}
After adding the pfe to the routing and registering page types with it, the rendering of the pages needs to be setup.
The main entry point/router outlet for the page flow display is the PFE Master Page.
There are two versions available:
In general it is recommended to use the ndbx based one, if the app is ndbx based.
It can be used in the following way in the app.component.ts
:
<pfe-ndbx-master-page></pfe-ndbx-master-page>
Alternatively, it is also possible to create a custom setup/component for the pfe router outlet.
Translation of texts within the pfe is required for these use cases:
PfeNavButtonsContainerComponent
)If custom templates are used for this or no translation is needed, it is not necessary to import any translation adapter.
The PFE supports two different translation tools/libraries out of the box:
The usage of a translation tool/library is activated by importing the respective module:
PFENgxTranslateAdapterModule
PFEAngularTranslateAdapterModule
The import has to be located below the NgxPfeModule
import. (The order of imports matters in Angular)
For example, like this:
NgxPfeModule.forRoot({
provide: NGX_PFE_CONFIGURATION,
useFactory: ngxPFEConfigFactory
}),
// Activate this import, to use ngx-translate
// PFENgxTranslateAdapterModule,
// Activate this import, to use the native Angular translate:
PFEAngularTranslateAdapterModule,
A prerequisite for the usage of those modules is the setup/configuration of the translate tool/library.
It is possible to add support for further translation tools/libraries by implementing a custom version of the PFETranslateService
.
It can be supplied as a provider:
{
provide: PFETranslateService,
useClass: MyCustomTranslateAdapterService,
}
If ngx-translate is used for the translations, the pfeI18nFromState
pipe and the pfeI18nFromState
directive can be used.
They take a value from the state and use it as a translation key with ngx-translate.
As they depend on ngx-translate, they are located in a secondary entry: @allianz/ngx-pfe/util/i18n-from-state
.
This functionality is not available with the Angular native translation, as dynamic translation keys are not supported by it.
In most cases, the ngx-pfe is integrated into the main routing entry point of an app. Alternatively, it is also possible to integrate it as a child into the existing routing of an app.
The integration itself is pretty much the same as in the routing root of an app. But there are a few caveats to be taken care of.
In this example, the pfe is integrated into the parent routing like this:
const routes: Routes = [
{ path: 'some-route', component: SomeComponent },
{
path: 'pfe-journey',
loadChildren: () => import('./pfe-journey/src/app/entry.module').then((m) => m.EntryModule),
},
{
// More routes
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
The EntryModule
for the journey imports the pfe, setups the routing and can also contain a wrapper around the pfe content.
It is very important, that the routing of the EntryModule
is setup in its own module and imported as the first module within the EntryModule
.
The routing module looks like this:
const routes: Routes = [
{
path: '',
component: EntryComponent,
children: [
{
path: '',
loadChildren: () => import('@allianz/ngx-pfe').then((m) => m.NgxPfeRoutingModule),
},
],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class EntryRoutingModule {}
This module is then imported in the EntryModule
:
@NgModule({
declarations: [EntryComponent],
imports: [
EntryRoutingModule,
NgxPfeModule.forRoot({
provide: NGX_PFE_CONFIGURATION,
useFactory: ngxPFEConfigFactory,
}),
],
providers: [],
exports: [RouterModule],
})
export class EntryModule {}
The EntryComponent
can then either use the master page from the pfe or define a custom wrapper/navigation and simply provide a router-outlet
.
The integration as a module in the parent routing has a catch that needs to be handled by the parent navigation.
If a user navigates to the pfe journey via a navigation menu that uses the Angular routerLink
, it is possible for the user to trigger that navigation again, while the pfe is already active.
This causes the Angular router to jump back to the initial route of the link and will break the journey.
Unfortunately, there is no way for the pfe to intercept and to prevent this from happening (Route Guards actually do not fire).
This means, the parent app needs to take care to deactivate the routerLink
when the journey is already active.
Luckily, this can be done with the routerLinkActive
functionality and a bit of css to disable the pointer-events
For example, with the following navigation link:
<a class="nav-link" routerLink="/pfe/journey" routerLinkActive="active"><span class="nav-text">Live Demo</span></a>
And this corresponding css to deactivate the link while it is active:
.nav-link.active {
pointer-events: none;
}