Strickland
Search…
Composition and formatResult
We learned about formatResult earlier. That validator wrapper provides an easy way to augment or transform the objectProps and arrayElements validation results to match the shape your application needs.
In the following example, formatResult is used to create a validationErrors validation result prop. The validationErrors value is flattened array of all validation errors existing in an object or array graph.
1
import validate, {arrayElements, formatResult} from 'strickland';
2
3
const withValidationErrors = (result) => {
4
const validationErrors = [];
5
6
function addErrorsFromObjectProps(resultObjectProps, parentPath) {
7
if (resultObjectProps) {
8
Object.keys(resultObjectProps)
9
// for each prop, get that prop's result
10
.map((propName) => ({
11
...resultObjectProps[propName],
12
propName
13
}))
14
15
// recursively add errors
16
.forEach(({propName, ...nestedResult}) => {
17
const propPath = [...parentPath, propName];
18
19
addErrorsFromResult({
20
...nestedResult,
21
propPath
22
});
23
});
24
}
25
}
26
27
function addErrorsFromArrayElements(resultArrayElements, parentPath) {
28
if (resultArrayElements) {
29
// recursively add errors
30
resultArrayElements.forEach((nestedResult, arrayElement) => {
31
const propPath = [...parentPath, arrayElement];
32
33
addErrorsFromResult({
34
...nestedResult,
35
propPath
36
});
37
});
38
}
39
}
40
41
function addErrorsFromResult(nestedResult) {
42
if (!nestedResult.isValid) {
43
const {
44
objectProps,
45
arrayElements,
46
propPath = [],
47
...errorResult
48
} = nestedResult;
49
50
// omit the `objectProps` and `arrayElements`
51
// result props but include `propPath`
52
validationErrors.push({propPath, ...errorResult});
53
54
addErrorsFromObjectProps(objectProps, propPath);
55
addErrorsFromArrayElements(arrayElements, propPath);
56
}
57
}
58
59
addErrorsFromResult(result);
60
61
return {
62
...result,
63
validationErrors
64
};
65
};
66
67
const validateWithErrors = formatResult(withValidationErrors, {
68
name: required(),
69
addresses: [required(), minLength(1), arrayElements({
70
addressType: required(),
71
street: [required(), {
72
number: required(),
73
name: required()
74
}],
75
city: [required()],
76
state: [required(), length(2, 2)],
77
postal: [required(), length(5, 5)]
78
})]
79
});
80
81
const data = {
82
name: 'Marty',
83
addresses: [
84
{
85
addressType: 'Home',
86
street: {
87
number: 9303,
88
name: 'Lyon Drive'
89
},
90
city: 'Hill Valley',
91
state: 'CA'
92
},
93
{
94
addressType: 'Work'
95
}
96
]
97
};
98
99
const result = validate(validateWithErrors, data);
100
101
expect(result).toMatchObject({
102
validationErrors: [
103
expect.objectContaining({
104
value: expect.objectContaining({
105
name: 'Marty',
106
addresses: expect.any(Array)
107
})
108
}),
109
expect.objectContaining({
110
propPath: ['addresses']
111
}),
112
expect.objectContaining({
113
propPath: ['addresses', 0],
114
value: expect.objectContaining({
115
addressType: 'Home'
116
})
117
}),
118
expect.objectContaining({
119
propPath: ['addresses', 0, 'postal'],
120
required: true
121
}),
122
expect.objectContaining({
123
propPath: ['addresses', 1],
124
value: expect.objectContaining({
125
addressType: 'Work'
126
})
127
}),
128
expect.objectContaining({
129
propPath: ['addresses', 1, 'street'],
130
required: true
131
}),
132
expect.objectContaining({
133
propPath: ['addresses', 1, 'city'],
134
required: true
135
}),
136
expect.objectContaining({
137
propPath: ['addresses', 1, 'state'],
138
required: true
139
}),
140
expect.objectContaining({
141
propPath: ['addresses', 1, 'postal'],
142
required: true
143
})
144
]
145
});
146
147
/*
148
// Most result props are omitted for illustration
149
// of the validationErrors contents
150
result = {
151
isValid: false,
152
// objectProps,
153
// value,
154
validationErrors: [
155
{
156
value: {
157
name: 'Marty',
158
addresses: [/* ... */]
159
})
160
},
161
{
162
propPath: ['addresses']
163
},
164
{
165
propPath: ['addresses', 0],
166
value: {
167
addressType: 'Home'
168
}
169
},
170
{
171
propPath: ['addresses', 0, 'postal'],
172
required: true
173
},
174
{
175
propPath: ['addresses', 1],
176
value: {
177
addressType: 'Work'
178
}
179
},
180
{
181
propPath: ['addresses', 1, 'street'],
182
required: true
183
},
184
{
185
propPath: ['addresses', 1, 'city'],
186
required: true
187
},
188
{
189
propPath: ['addresses', 1, 'state'],
190
required: true
191
},
192
{
193
propPath: ['addresses', 1, 'postal'],
194
required: true
195
}
196
]
197
}
198
*/
Copied!
Last modified 1yr ago
Copy link