Skip to content

Commit db319f4

Browse files
Merge pull request #15 from Balastrong/support-bracket-notation
feat: support bracket notation for arrays
2 parents a076e55 + e0fbdd5 commit db319f4

File tree

4 files changed

+73
-6
lines changed

4 files changed

+73
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes to the library will be documented in this file.
44

5+
## vX.X.X (Month DD, YYYY)
6+
7+
- Add support for array bracket notation (pull request #15)
8+
59
## v0.7.5 (June 01, 2024)
610

711
- Republish previous version because build step was forgotten

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ import { decode } from 'https://deno.land/x/decode_formdata/mod.ts';
3131

3232
`FormData` stores the names of your fields and their values. However, there is a problem. Only strings and files are accepted as values, but complex forms can contain booleans, strings and dates. This leads to the fact that the boolean value `true` must be mapped with `"on"` and `false` values are simply ignored. Numbers and dates are also converted to strings.
3333

34-
Another problem are objects and arrays, which are usually mapped using dot notation. For example, the input field `<input name="todos.0.label" />` should map to the object `{ todos: [{ label: "" }] }`. By telling `decode` where arrays, booleans, dates, files, and numbers are located, the function can decode your `FormData` back into a complex JavaScript object.
34+
Another problem are objects and arrays, which are usually mapped using dot and bracket notation. For example, the input field `<input name="todos.0.label" />` should map to the object `{ todos: [{ label: "" }] }`. By telling `decode` where arrays, booleans, dates, files, and numbers are located, the function can decode your `FormData` back into a complex JavaScript object.
35+
36+
> Both dot and bracket notation are supported for arrays.
3537
3638
Consider the following form to add a new product to an online store:
3739

@@ -174,3 +176,7 @@ Find a bug or have an idea how to improve the library? Please fill out an [issue
174176
## License
175177

176178
This project is available free of charge and licensed under the [MIT license](https://github.com/fabian-hiller/decode-formdata/blob/main/LICENSE.md).
179+
180+
## Note
181+
182+
Both dot and bracket notation are supported for arrays.

src/decode.test.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ describe('decode', () => {
6464
expect(decode(formData, { files: ['file'] })).toEqual({ file });
6565
});
6666

67-
test('should decode indexed arrays', () => {
67+
test('should decode indexed arrays with dot notation', () => {
6868
const formData = new FormData();
6969
formData.append('array.0', 'index_0');
7070
formData.append('array.1', 'index_1');
@@ -74,6 +74,16 @@ describe('decode', () => {
7474
});
7575
});
7676

77+
test('should decode indexed arrays with bracket notation', () => {
78+
const formData = new FormData();
79+
formData.append('array[0]', 'index_0');
80+
formData.append('array[1]', 'index_1');
81+
formData.append('array[2]', 'index_2');
82+
expect(decode(formData, { arrays: ['array'] })).toEqual({
83+
array: ['index_0', 'index_1', 'index_2'],
84+
});
85+
});
86+
7787
test('should decode non-indexed arrays', () => {
7888
const formData = new FormData();
7989
formData.append('array', 'index_0');
@@ -88,7 +98,7 @@ describe('decode', () => {
8898
});
8999
});
90100

91-
test('should decode numbers in array', () => {
101+
test('should decode numbers in array with dot notation', () => {
92102
const formData = new FormData();
93103
formData.append('array.0', '111');
94104
formData.append('array.1', '222');
@@ -100,6 +110,18 @@ describe('decode', () => {
100110
});
101111
});
102112

113+
test('should decode numbers in array with bracket notation', () => {
114+
const formData = new FormData();
115+
formData.append('array[0]', '111');
116+
formData.append('array[1]', '222');
117+
formData.append('array[2]', '333');
118+
expect(
119+
decode(formData, { arrays: ['array'], numbers: ['array[$]'] })
120+
).toEqual({
121+
array: [111, 222, 333],
122+
});
123+
});
124+
103125
test('should decode objects', () => {
104126
const formData = new FormData();
105127
formData.append('nested.string', 'hello');
@@ -108,7 +130,7 @@ describe('decode', () => {
108130
});
109131
});
110132

111-
test('should decode nested arrays', () => {
133+
test('should decode nested arrays with dot notation', () => {
112134
const formData = new FormData();
113135
formData.append('nested.0.array.0', 'index_0');
114136
formData.append('nested.0.array.1', 'index_1');
@@ -123,6 +145,21 @@ describe('decode', () => {
123145
});
124146
});
125147

148+
test('should decode nested arrays with bracket notation', () => {
149+
const formData = new FormData();
150+
formData.append('nested[0].array[0]', 'index_0');
151+
formData.append('nested[0].array[1]', 'index_1');
152+
formData.append('nested[0].array[2]', 'index_2');
153+
expect(
154+
decode(formData, {
155+
arrays: ['nested[$].array', 'empty.array'],
156+
})
157+
).toEqual({
158+
nested: [{ array: ['index_0', 'index_1', 'index_2'] }],
159+
empty: { array: [] },
160+
});
161+
});
162+
126163
test('should transform value', () => {
127164
const formData = new FormData();
128165
formData.append('string', 'hello');

src/decode.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,39 @@ export function decode<
4343
const [info, transform] =
4444
typeof arg2 === 'function' ? [undefined, arg2] : [arg2, arg3];
4545

46+
// Normalize info arrays to dot notation
47+
if (info) {
48+
for (const key of [
49+
'arrays',
50+
'booleans',
51+
'dates',
52+
'files',
53+
'numbers',
54+
] as const) {
55+
if (info[key]?.length) {
56+
info[key] = info[key]!.map((templateName) =>
57+
templateName.replace(/\[\$\]/g, '.$')
58+
);
59+
}
60+
}
61+
}
62+
4663
// Create empty values object
4764
const values: any = {};
4865

4966
// Add each form entry to values
5067
for (const [path, input] of formData.entries()) {
68+
// Normalize path to dot notation
69+
const normlizedPath = path.replace(/\[(\d+)\]/g, '.$1');
70+
5171
// Create template name and keys
52-
const templateName = path
72+
const templateName = normlizedPath
5373
.replace(/\.\d+\./g, '.$.')
5474
.replace(/\.\d+$/, '.$');
5575
const templateKeys = templateName.split('.');
5676

5777
// Add value of current field to values
58-
path.split('.').reduce((object, key, index, keys) => {
78+
normlizedPath.split('.').reduce((object, key, index, keys) => {
5979
// If it is not last index, return array or object
6080
if (index < keys.length - 1) {
6181
// If array or object already exists, return it

0 commit comments

Comments
 (0)