@dwp/casa-looper-plugin
v1.4.5
Published
Catch multiple instances of data by looping over a journey segment
Downloads
553
Readme
CASA Looper Plugin
This plugin provides a method of implementing the "Add Another Thing" pattern described in the DWP Design System.
Usage
This plugin requires that you create at least 2 separate Express sub-apps:
- One for your "parent" app that houses your main Plan, and
- One for the "looper" app that captures data multiple times
You must also mount your looper CASA app on a parameterised route (see below):
import express from 'express';
import { MemoryStore } from 'express-session';
import { configure } from '@dwp/govuk-casa';
import { looper, looperParent } from '../plugins/looper/index.js';
// Mount URLs for the "main" Plan, and the "loop" Plan
const MOUNT_MAIN = '/main/';
const MOUNT_LOOP = '/loop/';
// Both apps must share a session
const session = {
secret: 'secret',
store: new MemoryStore(),
};
// Create a CASA app for your looping journey
const { mount: mountLoop } = configure({
session,
plugins: [
looper({
// The waypoint on your "parent" journey that will display a summary of
// all captured data from each loop across the "looper" Plan
seedWaypoint: 'summary',
// The URL on which your "parent" app is mounted
parentMountUrl: MOUNT_MAIN,
// The last waypoint in your "looper" Plan. All journeys in the looper
// must end on this waypoint.
lastWaypoint: 'check-your-loop-answers',
}),
],
});
const loopApp = mountLoop(express(), {
route: '/:contextid', // Must use "contextid" parameter here
});
// Create a CASA app for your main, "parent" Plan
const { mount: mountMain } = configure({
session,
plugins: [
looperParent({
// As above
seedWaypoint: 'summary',
// The URL on which your "looper" app is mounted
looperMountUrl: MOUNT_LOOP,
// The last waypoint in your "looper" Plan. All journeys in the looper
// must end on this waypoint.
lastWaypoint: 'check-your-loop-answers',
}),
],
});
const mainApp = mount(express());
// Mount everything into a top-level app
const app = express();
app.use(MOUNT_MAIN, mainApp);
app.use(MOUNT_LOOP, loopApp);
app.listen();
Templates
In your "seed" waypoint template, you will have the following variables available:
| Variable | Description |
|----------|-------------|
| loopContexts[]
| Array of ephemeral JourneyContext
instances associated with this seed waypoint. These are additionally decorated with attributes below ... |
| loopContexts[].editUrl
| Send the user to this URL to edit the context. You can use waypointUrl()
to generate your own URL instead, but this default link will take you to the last page in the loop journey, assuming it is the "check your answers" summary for the loop questions. Refer to the "Add Another Thing" pattern for details on how this may need altering for you use-case. |
| loopContexts[].removeUrl
| Send the user to this URL so they can confirm removal of this contest |
| loopContexts[].isComplete
| If the user has reached the last waypoint in the loop Plan, then the context will be flagged as "complete". Incomplete contexts will prevent the user from progressing beyond the seed waypoint. |
| createUrl
| Send the user to this URL to create a new loop context |
Hooks
The plugin provides a couple of extra hooks that you can use on the looping journey app. These are just Express middleware functions
| Hook | Description |
|------|-------------|
| looper.remove_prerender
| Called just before the "remove" page is rendered. Executes on both GET
and POST
requests |
| looper.remove_preredirect
| Called just before the user is redirected after removing (or cancelling the removal) of a loop context. Executed on POST
requests |
Example usage:
const { mount } = configure({
session,
plugins: [
looper({
seedWaypoint: 'summary',
parentMountUrl: '/main/',
lastWaypoint: 'check-your-loop-answers',
}),
],
hooks: [{
hook: 'looper.remove_prerender',
middleware: (req, res, next) => {
// E.g. add some variables to `res.locals`
next();
}
}],
});
Handling missing looper contexts
Sometimes, a journey context will no longer be available to the loop app; for example if the user removes a loop entry and then attempts to navigate back to that removed entry. In such cases, the user will be redirected to a /not-found
route on your loop's mount point.
You can intercept this route handler to replace it with your own error page or route behaviours:
ancillaryRouter.prependUse('/not-found', (req, res, next) => {
console.log('Intercepting /not-found route');
next();
});
Notes
showSeedWaypointFirst
By default, the "seed waypoint" will be skipped on the first iteration, and only shown once that iteration is complete.
You can alter this behaviour by using the showSeedWaypointFirst
flag on the looper parent application. This allows you, for example, to populate the seed waypoint template with data pulled from other sources (e.g. external APIs) before the user can then choose to "add another".
looperParent({
// ...
showSeedWaypointFirst: true
})
Different design patterns
Documentation is available here to explain how to use different design patterns
for the Add another item
page.
Customising seed waypoint
Documentation is available here to explain how the seed waypoint (Add another item
page) can be customised.
Nested loops
This plugin does not currently support nested loops, so you cannot attach loop journeys to another loop journey. We have to draw the line somewhere!