hof 20.0.4 → 20.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
|
|
1
|
+
# Combine & Loop Fields Behaviour
|
2
|
+
|
3
|
+
## What this does
|
4
|
+
This allows you to specify fields to loop over and add as objects to a parent array. You can use this for adding multiple addresses, criminal offences, names etc. You can see here in this example, we ask for a set of details prefixed with the word `storage-` for getting address details for multiple storage addresses (see the Home Office's firearms repository). We then can aggregate these addresses as objects into an array called `all-storage-addresses` and get redirected back to `/add-address` whenever we selected `yes` to adding another address,
|
5
|
+
```
|
6
|
+
'/add-address': {
|
7
|
+
fields: [
|
8
|
+
'storage-building',
|
9
|
+
'storage-street',
|
10
|
+
'storage-townOrCity',
|
11
|
+
'storage-postcodeOrZIPCode'
|
12
|
+
],
|
13
|
+
next: '/add-another-address-with-list',
|
14
|
+
continueOnEdit: true
|
15
|
+
},
|
16
|
+
'/add-another-address-with-list': {
|
17
|
+
template: 'add-another-address-loop.html',
|
18
|
+
behaviours: CombineAndLoopFields({
|
19
|
+
groupName: 'all-storage-addresses',
|
20
|
+
fieldsToGroup: [
|
21
|
+
'storage-building',
|
22
|
+
'storage-street',
|
23
|
+
'storage-townOrCity',
|
24
|
+
'storage-postcodeOrZIPCode'
|
25
|
+
],
|
26
|
+
removePrefix: 'storage-',
|
27
|
+
combineValuesToSingleField: 'address',
|
28
|
+
returnTo: '/add-address'
|
29
|
+
}),
|
30
|
+
next: '/confirm'
|
31
|
+
```
|
32
|
+
Here are the fields you call this behaviour first to set config for it:
|
33
|
+
```
|
34
|
+
`groupName`: (Required) a parent array for storing details for each object you are collecting information for,
|
35
|
+
`fieldsToGroup`: (Required) the fields being specified for an object, e.g. house number, street, postcode, that are grouped together,
|
36
|
+
`removePrefix`: (Optional) a string which is used to remove consistent prefixes from a collection of fields that are grouped together,
|
37
|
+
`combineValuesToSingleField`: (Optional) a new field that is created with its value being the concatenation of values of the fields specified in `fieldsToGroup`,
|
38
|
+
`returnTo`: the next step if you want to add another object to this group
|
39
|
+
```
|
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
const _ = require('lodash');
|
3
|
+
const uuid = require('uuid').v1;
|
4
|
+
const path = require('path');
|
5
|
+
const express = require('express');
|
6
|
+
|
7
|
+
module.exports = config => {
|
8
|
+
const { returnTo, groupName, fieldsToGroup, combineValuesToSingleField, removePrefix } = config;
|
9
|
+
|
10
|
+
if (removePrefix && typeof removePrefix !== 'string') {
|
11
|
+
throw new Error('removePrefix is a string and is optional for loops');
|
12
|
+
}
|
13
|
+
|
14
|
+
if (combineValuesToSingleField && typeof combineValuesToSingleField !== 'string') {
|
15
|
+
throw new Error('combineValuesToSingleField is a string and is optional for loops');
|
16
|
+
}
|
17
|
+
|
18
|
+
if (!returnTo || typeof returnTo !== 'string') {
|
19
|
+
throw new Error('returnTo is a string and is required for loops');
|
20
|
+
}
|
21
|
+
|
22
|
+
if (!groupName || typeof groupName !== 'string') {
|
23
|
+
throw new Error('groupName is a string and is required for loops');
|
24
|
+
}
|
25
|
+
|
26
|
+
if (!fieldsToGroup ||
|
27
|
+
!fieldsToGroup.length ||
|
28
|
+
!Array.isArray(fieldsToGroup) ||
|
29
|
+
_.some(fieldsToGroup, field => typeof field !== 'string')) {
|
30
|
+
throw new Error('fieldsToGroup is an array of strings and is required for loops');
|
31
|
+
}
|
32
|
+
|
33
|
+
return superclass => class extends superclass {
|
34
|
+
get(req, res, next) {
|
35
|
+
if (req.query.delete) {
|
36
|
+
const router = express.Router({ mergeParams: true });
|
37
|
+
router.use([
|
38
|
+
// eslint-disable-next-line no-underscore-dangle
|
39
|
+
this._configure.bind(this),
|
40
|
+
this.removeItem.bind(this),
|
41
|
+
this.reload.bind(this)
|
42
|
+
]);
|
43
|
+
return router.handle(req, res, next);
|
44
|
+
}
|
45
|
+
return super.get(req, res, next);
|
46
|
+
}
|
47
|
+
|
48
|
+
getLoopFields(req) {
|
49
|
+
let loopedFields = _.pick(req.sessionModel.toJSON(), fieldsToGroup);
|
50
|
+
|
51
|
+
if (removePrefix) {
|
52
|
+
loopedFields = _.mapKeys(loopedFields, (value, key) => key.replace(removePrefix, ''));
|
53
|
+
}
|
54
|
+
return loopedFields;
|
55
|
+
}
|
56
|
+
|
57
|
+
removeItem(req, res, next) {
|
58
|
+
const id = req.query.delete;
|
59
|
+
const items = req.sessionModel.get(groupName).filter(item => item.id !== id);
|
60
|
+
req.sessionModel.set(groupName, items);
|
61
|
+
next();
|
62
|
+
}
|
63
|
+
|
64
|
+
// eslint-disable-next-line no-unused-vars
|
65
|
+
reload(req, res, next) {
|
66
|
+
const items = req.sessionModel.get(groupName);
|
67
|
+
if (!items.length) {
|
68
|
+
req.sessionModel.set(`${groupName}-saved`, false);
|
69
|
+
fieldsToGroup.forEach(field => {
|
70
|
+
req.sessionModel.unset(field);
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
const target = items.length ? req.form.options.route : returnTo;
|
75
|
+
const action = req.params.action || '';
|
76
|
+
res.redirect(path.join(req.baseUrl, target, action));
|
77
|
+
}
|
78
|
+
|
79
|
+
configure(req, res, next) {
|
80
|
+
const field = `${groupName}-add-another`;
|
81
|
+
// add yes/no field
|
82
|
+
req.form.options.fields[field] = Object.assign({
|
83
|
+
mixin: 'radio-group',
|
84
|
+
validate: ['required'],
|
85
|
+
options: [
|
86
|
+
'yes', 'no'
|
87
|
+
]
|
88
|
+
}, req.form.options.fieldSettings);
|
89
|
+
|
90
|
+
// add conditonal fork
|
91
|
+
req.form.options.forks = req.form.options.forks || [];
|
92
|
+
req.form.options.forks.push({
|
93
|
+
target: returnTo,
|
94
|
+
continueOnEdit: true,
|
95
|
+
condition: {
|
96
|
+
field: field,
|
97
|
+
value: 'yes'
|
98
|
+
}
|
99
|
+
});
|
100
|
+
next();
|
101
|
+
}
|
102
|
+
|
103
|
+
getValues(req, res, next) {
|
104
|
+
const fieldsGroup = req.sessionModel.get(groupName) || [];
|
105
|
+
const added = req.sessionModel.get(`${groupName}-saved`);
|
106
|
+
return super.getValues(req, res, (err, values) => {
|
107
|
+
if (err) {
|
108
|
+
return next(err);
|
109
|
+
}
|
110
|
+
if (!added) {
|
111
|
+
const fields = this.getLoopFields(req);
|
112
|
+
if (!_.isEmpty(fields)) {
|
113
|
+
const newField = Object.assign({id: uuid()}, fields);
|
114
|
+
|
115
|
+
if (combineValuesToSingleField) {
|
116
|
+
const combinedValues = _.filter(fieldsToGroup.map(field => req.sessionModel.get(field))).join(', ');
|
117
|
+
newField[combineValuesToSingleField] = combinedValues;
|
118
|
+
}
|
119
|
+
|
120
|
+
fieldsGroup.push(newField);
|
121
|
+
values[groupName] = fieldsGroup;
|
122
|
+
fieldsToGroup.forEach(field => req.sessionModel.unset(field));
|
123
|
+
|
124
|
+
req.sessionModel.set(groupName, fieldsGroup);
|
125
|
+
req.sessionModel.set(`${groupName}-saved`, true);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
return next(null, values);
|
129
|
+
});
|
130
|
+
}
|
131
|
+
|
132
|
+
locals(req, res) {
|
133
|
+
const items = req.form.values[groupName] || [];
|
134
|
+
return Object.assign({}, super.locals(req, res), {
|
135
|
+
items,
|
136
|
+
hasItems: items.length > 0,
|
137
|
+
field: groupName
|
138
|
+
});
|
139
|
+
}
|
140
|
+
|
141
|
+
saveValues(req, res, next) {
|
142
|
+
// remove "yes" value from session so it is no pre-populated next time around
|
143
|
+
super.saveValues(req, res, err => {
|
144
|
+
const field = `${groupName}-add-another`;
|
145
|
+
if (req.form.values[field] === 'yes') {
|
146
|
+
req.sessionModel.unset(field);
|
147
|
+
req.sessionModel.set(`${groupName}-saved`, false);
|
148
|
+
}
|
149
|
+
next(err);
|
150
|
+
});
|
151
|
+
}
|
152
|
+
};
|
153
|
+
};
|