This module is now deprecated and has been superceded by the following modules:
- hof-behaviour-summary-page replaces confirm-controller
- hof-component-date replaces date-controller
The form, base and start controllers have been removed and functionality has been moved back into hof-form-wizard.
Please see the hof guide for more information.
A collection of controllers extended from passports-form-wizard Wizard, Form Controller:
require('hmpo-form-wizard').Controller
var controllers = require('hof-controllers');
Accessed as base
from hof-controllers
var baseController = require('hof-controllers').base;
Extends from passports-form-wizard Wizard, Form Controller.
{
clearSession: true,
/* step options */
}
In the wizard options
hofWizard(steps, fields, {
/* wizard options */
params: '/:action?'
});
In the view template
a href='page_name/edit'
Or override in step options
{
continueOnEdit: true
/* step options */
}
Adds single
or multiple
to the locals to describe the number of errors for pluralisation of error messages.
- Exposes
route
name to template (without preceding slash) - Exposes
title
to template if found in translations. Looked up in the order:pages.{route}.title -> fields.{firstFieldName}.label -> fields.{firstFieldName}.legend
. - Exposes
intro
to template if found in translations atpages.{route}.intro
Add a locals object to step config to expose configurable key/value pairs in the template. Useful for generating template partials programmatically. These will override any locals provided further up the tree.
Steps config
'/step-name': {
locals: {
pageTitle: 'Page Title'
foo: 'bar'
}
}
Template
<h1>{{pageTitle}}</h1>
<div class="{{foo}}"></div>
Fields given in step config will be exposed to the template along with a mixin if defined in field config. This can be used with the renderField mixin to programmatically generate templates.
steps.js
steps: {
'step-1': {
fields: [
'field-1',
'field-2'
]
}
}
fields.js
fields: {
'field-1': {
mixin: 'input-text',
...
},
'field-2': {
mixin: 'radio-group',
...
}
}
exposed to templates in format:
fields: [{
key: 'field-1',
mixin: 'input-text'
}, {
key: 'field-2',
mixin: 'radio-group'
}]
Each step definition accepts a next
property, the value of which is the next route in the journey. By default, when the form is successfully submitted, the next steps will load. However, there are times when it is necessary to fork from the current journey based on a users response to certain questions in a form. For such circumstances there exists the forks
property.
In this example, when the submits the form, if the field called 'example-radio' has the value 'superman', the page at '/fork-page' will load, otherwise '/next-page' will be loaded.
'/my-page': {
next: '/next-page',
forks: [{
target: '/fork-page',
condition: {
field: 'example-radio',
value: 'superman'
}
}]
}
The condition property can also take a function. In the following example, if the field called 'name' is more than 30 characters in length, the page at '/fork-page' will be loaded.
'/my-page': {
next: '/next-page',
forks: [{
target: '/fork-page',
condition: function (req, res) {
return req.form.values['name'].length > 30;
}
}]
}
Forks is an array and therefore each fork is interrogated in order from top to bottom. The last fork whose condition is met will assign its target to the next page variable.
In this example, if the last condition resolves to true - even if the others also resolve to true - then the page at '/fork-page-three' will be loaded. The last condition to be met is always the fork used to determine the next step.
'/my-page': {
next: '/next-page',
forks: [{
target: '/fork-page-one',
condition: function (req, res) {
return req.form.values['name'].length > 30;
}
}, {
target: '/fork-page-two',
condition: {
field: 'example-radio',
value: 'superman'
}
}, {
target: '/fork-page-three',
condition: function (req, res) {
return typeof req.form.values['email'] === 'undefined';
}
}]
}
Accessed as
from hof-controllers
var dateController = require('hof-controllers').date;
Extends from require('hof-controllers').base;
-
Validates the dates as a single item.
-
Date validators default to:
required
,numeric
,format
(DD-MM-YYYY
), andfuture
. -
What the validators the date validates against can be overridden with the
validate
property on the date key field.
In this example, the 'my-date'
fields will only validate if they contain non-numeric characters.
{
'my-date': {
validate: ['numeric']
}
}
Note: In the preceding example the field is not required and will not error on empty values.
If you want a shared date field to be required, but on a particular page wish it to be optional, validateField
will accept a third parameter called isRequired
.
This will allow the date field to be optional unless the user enters a value, in which case an appropriate message will be shown.
MyController.prototype.validateField = function validateField(keyToValidate, req) {
return DateController.prototype.validateField.call(this, keyToValidate, req, false);
};
- Adds a 'pretty' formatted (
D MMMM YYYY
) date to the form values.
A simple wrapper around require('hmpo-form-wizard').Error;
to make it easier to extend and customise error behaviour on error.
To extend the functionality of a controller call the parent constructor and use node util
to inherit the prototype;
this.dateKey
is the value of the date field that the controller will process. The value of the this.dateKey
must match the name of the date field.
Read more about date fields
var DateController = function DateController() {
this.dateKey = 'my-date';
Controller.apply(this, arguments);
};
util.inherits(DateController, Controller);
Extends the base controller's locals method to provide data in a format suitable for generating a summary table and email.
Accessed as confirm
from hof-controllers
.
var confirmController = require('hof-controllers').confirm;
Extends from require('hof-controllers').base
In step options
'/confirm': {
controller: require('hof-controllers').confirm,
fieldsConfig: require('./path/to/fields/config'),
emailConfig: require('../../config').email,
customerEmailField: 'email-address' // the id of the user's email address field
}
In config page template
{{#rows}}
{{> partials-summary-table}} <!-- {{name}}, {{value}}, {{origValue}} and {{step}} are available in this scope -->
{{/rows}}
This assumes all steps containing fields have a section
- locals.section
, this is used to group fields in the confirm table and the email.
Translations will be looked up automatically if located at the correct path. For section headers this is pages.{section}.summary
which falls back to pages.{section}.header
.
For fields the path is fields.{field}.summary
which falls back firstly to fields.{field}.label
then to fields.{field}.legend
.
If the lookup fails the the section id or the field id are used.
The renderField mixin can be called in your template with the field to render as the scope. This will lookup the field.mixin in res.locals and call it passing the field key.
{{#fields}}
{{#renderField}}{{/renderField}}
{{/fields}}
renderField
supports conditionally omitting fields if useWhen
is passed in field config. useWhen
accepts another field key String
and checks the value is true
, or an Object
with the keys field
and value
. The field to check cannot appear on the same step - consider using the toggle
property to show/hide a field on the same step.
'field-1': {
useWhen: 'field-2'
}
field-1
will only be included if field-2
value is true
'field-3': {
useWhen: {
field: 'field-4',
value: 'a-value'
}
}
field-3
will only be included if field-4
value is a-value
When a field on a multiple-step form is only to be included depending on the outcome of a previous answer. In the below example dependant-field
is only included on step-2 if dependent-radio
on step-1 is 'yes'
;
steps.js
{
'/step-1': {
fields: [
'dependent-radio'
]
},
'/step-2': {
fields: [
'dependant-field',
'regular-field'
]
}
}
fields.js
{
'dependent-radio': {
options: ['yes', 'no']
},
'dependant-field': {
useWhen: {
field: 'dependant-field',
value: 'yes'
}
},
'regular-field': {}
}
$ npm test