State Storage

The PfeBusinessService is used to store and retrieve data in the application. The state is accessed in the configuration for the navigation and service activators. Additionally it also provides programmatic access via observables for keys/expressions.

1. State expressions

To access values in the PFE state or any other JSON structure (like Service Activator responses) you will be using "expressions". These expressions are built on JSONPath, a querying language for JSON similar to XPath for XML. The expressions you can use in the PFE configuration exactly follow the rules of JSONPath expressions so please take a look at the documentation available at the previous link or the corresponding npm package.

A JSONPath expression always returns an array. In most cases, this array only contains a single element as the result of a query. The PFE will automatically only return that single value, to make the usage of the expressions easier. This is not surprising when you imagine queries that access a single property in the state like $.myCustomKey. In case you have a more generic query that includes more advanced operators like "array slice" or "recursive descendant" than you will typically expect to get an array back. However, in this case the mentioned logic applies as well: If the result is a single value than you will receive that single value.

Let's look at an example to see why this matters:
Imagine our state contains an array of numbers at $.numbers. The expression $.numbers[::2] can be used to query every second number (take 1, skip 1, take 1, skip 1, ...). If we only have 2 numbers in the state this query will only yield a single number (the first one). By default all arrays that contain only a single element will be unwrapped by the PFE so as a result you would receive a single number (i.e. a return value of type number) - even though you asked for an array of numbers. If, however, the state contains 3 numbers that same query would yield an array with 2 elements. That means that, depending on the PFE state, you would receive a different type of data (number vs. Array<number>) which might add complexity to the receiving end of that query.

As shown there are certain expressions where it's reasonable to always expect an array. To overcome the default unwrapping-behavior you can instruct the PFE to keep the result as an array. To do so use the special array root []$ in your expression. For the above example: The query []$.numbers[::2] will always result in an array. If the result of a query is already an array than this syntax won't wrap it inside another array, so you don't need to fear nested arrays.

💡 This affects all queries. So even the return value for a very specific query like $.myCustomKey can be turned into an array using the array syntax: []$.myCustomKey.

2. Update State from Config

There is a possibility to update the state storage from the config, for that purpose you can define a list of updates that can be performed. It is also possible to apply those only with certain conditions.

  • StateUpdates
  • StateOperations

3. Update State from navigation

It is possible to update the states values as part of the nextOptionList & backOptionList, as you can see in the example:

 {
      pageId: 'secondPageID',
      nextOptionList: [
        {
          nextPageId: 'thirdPageID',
          updateStateValues: [
            {
              key: `$.user`,
              value: 'Tobias',
            }
          ]
        }
      ],
      backOptionList: [
        {
          nextPageId: 'welcomePage',
          updateStateValues: [
            {
              key: `$.user`,
              operation: StateOperations.REMOVE,
              conditions: [
                {
                  type: "PFE_EXPRESSION",
                  expression: '{$.inputFieldID} === "reset"'
                }
              ],
            }
          ]
        }
      ]
    }

4. Update State from pageConfig

The same behavior is available as part of the page config.

const examplePageConfig: DemoAppPageConfig = {
  pageId: 'welcomePage',
  hideNextButton: false,
  hideBackButton: true,
  updateStateValues: [
    {
      key: `$.welcomePageReached`,
      value: true,
    },
  ],
  examplePageConfig: {
    firstConfigValue: 'This is the Page Title from the Configuration',
    secondConfigValue: 'Another text from configuration',
  },
};

Further examples of more complex configurations can be found in the viewer app of the pfe.

5. Update State from Components

this.pfeBusinessService.storeValueByExpression('$.mykey', 'value');

this.pfeBusinessService.getValueByExpression('$.myKey');

this.pfeBusinessService
    .getObservableForExpressionKey('$.mykey').subscribe(data => ...)

6. Storing and Restoring the State

6.1. Storage Locations

Three different storage locations are supported:

  • The local sessionStorage of the browser. The sessionStorage is automatically restored on page load.
  • The remote state service. In this case the state is restored via a URL with a stateID parameter.
  • The NGX_PFE_INITIAL_STATE InjectionToken

Browser sessionStorage

To enable the sessionStorage as a state storage location, add the following to the pfe appConfiguration:

{
  "appConfiguration": {
    "pfeConfig": {
      "enableSessionStorageState": true
    }
  }
}

The state is then stored under the state key in the sessionStorage. When the app is loaded again, the state is automatically restored from the sessionStorage.

Remote State Service

The remote storage allows to restore the state at a later point, when the application is used in a different browser session. An example use case is to continue a customer journey at a later point, via a link.

There is a spring boot starter plugin available that stores it in a Redis database. There is also an alternative version of the PFE state service available, that stores the states in Dynamodb.

The endpoint needs to be configured via the stateServiceEndpoint attribute the NgxPfeModuleConfiguration to use the remote state service. Usually, this is a part of the module config factory:

export function ngxPFEConfigFactory() {
  const ngxPfeModuleConfiguration: NgxPfeModuleConfiguration = {
    stateServiceEndpoint: 'http://localhost:8080/client-state',
  };
  return ngxPfeModuleConfiguration;
}

Restoring a Remote State

When the state service endpoint is configured, the pfe will automatically initialize a new remote state during startup. The service then returns a state ID, which is stored in the state itself under the key $.pfeStateID

This ID can be used to restore the state at a later point.

The ID has to be added to the URL in the stateId parameter. For example: http://localhost:4200/?stateId=pfe-state8538201. The state is then automatically fetched from the service and restored during startup.

NGX_PFE_INITIAL_STATE InjectionToken

The optional NGX_PFE_INITIAL_STATE InjectionToken can be used to restore the state from any source outside the PFE.

It provides a promise, which has to be resolved with the state data to be restored. If the promise is rejected, a redirect to the error page is done. If the token is available, all other state restore sources are ignored.

Restoring the State to a Value

The state can be set to an already known value, like this:

@NgModule({
    imports: [
      NgxPfeModule.forRoot({
        provide: NGX_PFE_CONFIGURATION,
        useFactory: ngxPFEConfigFactory,
      }),
    ],
    providers: [
      {
        provide: NGX_PFE_INITIAL_STATE,
        useValue: Promise.resolve({
          timeFor: 'Kālai vaṇakkam!'
        }),
      },
    ],
  })

Restoring the State Asynchronously

The following pattern can be used to restore the state asynchronously. For example after fetching it from a remote service.

The pfe will wait for the promise to be resolved. Keep in mind, that this delays the startup of all pfe functionalities, which means that things like waiting animations might need to be triggered in another way.

@NgModule({
    imports: [
        NgxPfeModule.forRoot({
            provide: NGX_PFE_CONFIGURATION,
            useFactory: ngxPFEConfigFactory,
        }),
    ],
    providers: [
        {
            provide: NGX_PFE_INITIAL_STATE,
            useFactory: stateFactory
        },
    ],
})

function stateFactory() {
    // Fetch data from remote source
    return Promise.resolve({
        timeFor: 'S̄wạs̄dī txn b̀āy'
    }
    );
}

6.2. Trigger the Storing of a State

Configuring one of the state storage locations does not automatically store the state. To actually store a state, one of these two different trigger points needs to be used:

  • The update state action: PFE_UPDATE_STATE_ON_BACKEND
  • Automatically on every page switch

Via PFE Action

The PFE_UPDATE_STATE_ON_BACKEND action can be used to trigger the storing of the current state. For example:

{
  "onPageLeaveActions": [
    {
      "type": "PFE_UPDATE_STATE_ON_BACKEND"
    }
  ]
}

Optionally, it is also possible to set a specific pageID to be returned to, when the state is restored:

{
  "onPageLeaveActions": [
    {
      "type": "PFE_UPDATE_STATE_ON_BACKEND",
      "pageToReturnTo": "secondPageID"
    }
  ]
}

Automatically on every Page Switch

Alternatively, it is also possible to automatically store the state on every page switch.

The enableAutomaticStateStorage flag in the module config can be used to activate this functionality. For example:

export function ngxPFEConfigFactory() {
  const ngxPfeModuleConfiguration: NgxPfeModuleConfiguration = {
    enableAutomaticStateStorage: true,
    stateServiceEndpoint: 'http://localhost:8080/client-state',
  };
  return ngxPfeModuleConfiguration;
}

It is also possible to disable the state storage after a certain page in the flow. This can be used to exclude sensitive data from the backend storage.

results matching ""

    No results matching ""