Skip to content

Commit

Permalink
support dot style names for fields (e.g. settings.resolution)
Browse files Browse the repository at this point in the history
  • Loading branch information
serbanghita committed Jan 9, 2024
1 parent ea8d9c5 commit 0656c4c
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 8 deletions.
14 changes: 9 additions & 5 deletions src/FormToObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import {
isRadio, isSelectMultiple, isSelectSimple, isSubmitButton, isTextarea,
isUploadForm
} from "./dom";
import {extend, forEach, getLastIntegerKey, getNextIntegerKey, getObjLength} from "./utils";
import {
convertFieldNameToArrayOfKeys,
extend,
forEach,
getLastIntegerKey,
getNextIntegerKey,
getObjLength
} from "./utils";



export class FormToObject {
// Currently matching only fields like 'fieldName[...] or fieldName[]'.
public static keyRegex = /[^[\]]+|\[]/g;
public formSelector: HTMLFormElement | string = '';
public $form: HTMLFormElement | null = null;
public $formElements: HTMLFormField[] = [];
Expand Down Expand Up @@ -137,8 +142,7 @@ export class FormToObject {
}

// Extract all possible keys
// E.g. name="firstName", name="settings[a][b]", name="settings[0][a]"
objKeyNames = $domNode.name.match(FormToObject.keyRegex);
objKeyNames = convertFieldNameToArrayOfKeys($domNode.name);

if (objKeyNames && objKeyNames.length === 1) {
this.processSingleLevelNode($domNode, objKeyNames, (domNodeValue ? domNodeValue : ''), result);
Expand Down
18 changes: 18 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,21 @@ export function extend(settings: IFormToObjectOptions, source: IFormToObjectOpti
export function forEach<T extends Element>(arr: HTMLCollectionOf<T>, callback: (element: T, index?: number) => void) {
return Array.prototype.forEach.call(arr, callback);
}


export function convertFieldNameToArrayOfKeys(fieldName: string): string[] {

// Spring MVC field styles.
// Test for fields containing a dot (.) name="customer.address.zipcode"
if (fieldName.indexOf(".") !== -1) {
return fieldName.split(".");
}

// PHP style field names.
// Test for fields containing brackets ([]) 'fieldName[...] or fieldName[]'.
if (fieldName.indexOf("[") !== -1 && fieldName.indexOf("]") !== -1) {
return fieldName.match(/[^[\]]+|\[]/g) as string[];
}

return [fieldName];
}
30 changes: 30 additions & 0 deletions test/unit/checkbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ describe('checkbox', () => {
});
});

it('two checkboxes with the same name a.b and different values, both checked, should return value as an array', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="checkbox" name="a.b" value="first" checked/>
<input type="checkbox" name="a.b" value="second" checked/>
`;

const formToObject = new FormToObject($form);

expect(formToObject.convertToObj()).toEqual({
'a': {
'b': ["first", "second"]
}
});
});

it('checkboxes named checkbox[] should return an array of values, by default', () => {
const $form = document.createElement('form');
$form.innerHTML = `
Expand Down Expand Up @@ -98,6 +114,20 @@ describe('checkbox', () => {
checkbox: {"a": "a", "b": "b" }
});
});

it('checkboxes named checkbox.a and checkbox.b return an object', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="checkbox" name="checkbox.a" value="a" checked />
<input type="checkbox" name="checkbox.b" value="b" checked />
`;

const formToObject = new FormToObject($form);

expect(formToObject.convertToObj()).toEqual({
checkbox: {"a": "a", "b": "b" }
});
});
});


15 changes: 15 additions & 0 deletions test/unit/convertFieldNameToArrayOfKeys.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {convertFieldNameToArrayOfKeys} from "../../src/utils";

describe('convertFieldNameToArrayOfKeys', () => {
it('field name is just a simple word', () => {
expect(convertFieldNameToArrayOfKeys('fieldName')).toEqual(['fieldName']);
});

it('field name contains dot "."', () => {
expect(convertFieldNameToArrayOfKeys('a.b')).toEqual(['a', 'b']);
});

it('field name contains brackets "[]"', () => {
expect(convertFieldNameToArrayOfKeys('a[b][c][]')).toEqual(['a', 'b', 'c', '[]']);
});
});
37 changes: 36 additions & 1 deletion test/unit/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,42 @@ describe('input', () => {
expect(formToObject.convertToObj()).toEqual({'text':'value'});
});

it('multi-level', () => {
it('field containing dot', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="text" name="a.b" value="b">
<input type="text" name="a.bb.c" value="c">
`;
const formToObject = new FormToObject($form);

expect(formToObject.convertToObj()).toEqual({
'a': {
'b': 'b',
'bb': {
'c': 'c'
}
},
});
});

it('field containing dot with overlapping names', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="text" name="a.b" value="b">
<input type="text" name="a.b.c" value="c">
`;
const formToObject = new FormToObject($form);

expect(formToObject.convertToObj()).toEqual({
'a': {
'b': {
'c': 'c'
}
},
});
});

it('multi-level fields containing brackets []', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="text" name="matrix_one" value="a">
Expand Down
34 changes: 33 additions & 1 deletion test/unit/radio.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {FormToObject} from "../../src/FormToObject";

describe('radio', () => {
describe('unchecked radios', () => {
it('searched by a valid element string should return false', () => {
it('searched by a valid element string should return {}', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="radio" name="first" value="First value" />
Expand All @@ -15,6 +15,20 @@ describe('radio', () => {

expect(formToObject.convertToObj()).toEqual({});
});

it('field containing dot (.)', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="radio" name="person.choice" value="First value" />
<input type="radio" name="person.choice" value="Second value" />
<input type="radio" name="person.choice" value="Third value" />
<input type="radio" name="person.choice" value="Forth value" />
<input type="radio" name="person.choice" value="Fifth value" />
`;
const formToObject = new FormToObject($form);

expect(formToObject.convertToObj()).toEqual({});
});
});

describe('radio elements checked', () => {
Expand All @@ -41,5 +55,23 @@ describe('radio', () => {
});
});

it('field containing dot (.)', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="radio" name="person.choice" value="First value" />
<input type="radio" name="person.choice" value="Second value" checked />
<input type="radio" name="person.choice" value="Third value" />
<input type="radio" name="person.choice" value="Forth value" />
<input type="radio" name="person.choice" value="Fifth value" />
`;
const formToObject = new FormToObject($form);

expect(formToObject.convertToObj()).toEqual({
'person': {
'choice': 'Second value'
}
});
});

});
});
2 changes: 1 addition & 1 deletion test/unit/select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('select', () => {
expect(formToObject.convertToObj()).toEqual({});
});

it('should return an empty array when no options are selected and include empty values option is true', () => {
it('should return an empty array when no options are selected and includeEmptyValuedElements:true', () => {
const formToObject = new FormToObject($form, {includeEmptyValuedElements: true});
expect(formToObject.convertToObj()).toEqual({'multiple':[]});
});
Expand Down
14 changes: 14 additions & 0 deletions test/unit/submit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ describe('submit', () => {
expect(formToObject.convertToObj()).toEqual({});
});

it('input with name a.b and includeSubmitButton: true', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<input type="submit" name="a.b" value="go">
`;
const formToObject = new FormToObject($form, {includeSubmitButton: true});

expect(formToObject.convertToObj()).toEqual({
'a': {
'b': 'go'
}
});
});

// Currently we don't support <button>. Should we?
it('button', () => {
expect(() => {
Expand Down
16 changes: 16 additions & 0 deletions test/unit/textarea.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ describe('textarea', ()=> {

expect(formToObject.convertToObj()).toEqual({'textarea':'textarea'});
});

it('field named a.b should return an object', () => {
const $form = document.createElement('form');
$form.innerHTML = `
<textarea name="a.b">textarea</textarea>
`;
const formToObject = new FormToObject($form);

expect(formToObject.convertToObj()).toEqual({
'a': {
'b': 'textarea'
}
});
});

it('should return {} when the field has no value', () => {
const $form = document.createElement('form');
$form.innerHTML = `
Expand All @@ -19,6 +34,7 @@ describe('textarea', ()=> {

expect(formToObject.convertToObj()).toEqual({});
});

it('should return field name with empty value when the field has no value and includeEmptyValuedElements: true', () => {
const $form = document.createElement('form');
$form.innerHTML = `
Expand Down

0 comments on commit 0656c4c

Please sign in to comment.