DEV Community

Cover image for Nested ExtJS Form Data
Luca Minuti
Luca Minuti

Posted on

Nested ExtJS Form Data

Introduction

When developing web applications, handling form data effectively is often an important aspect of the development process. This article explores how to transform flat form data into structured JSON objects with nested properties and arrays. While ExtJS has excellent built-in form capabilities, converting form values into complex JSON structures requires additional processing that isn't provided out of the box.

Forms

ExtJS has very powerful support for HTML Forms. It offers many components under the Ext.form.* namespace that can help build complex layouts and handle common needs. However, sometimes we want not only a specific layout but also a custom JSON structure from the form.

Let's take a look at the following image:

Simple form

This can be achieved with this code:

{ xtype: 'formpanel', title: 'Form', items: [{ name: 'name', label: 'Name', xtype: 'textfield', value: 'Luca' }, { xtype: 'fieldcontainer', label: 'Credit card', items: [{ xtype: 'textfield', name: 'brand', value: 'VISA' },{ xtype: 'textfield', name: 'number', flex: 1, value: '1234567890' }] }], buttons: [{ text: 'Submit', handler: ... }] } 
Enter fullscreen mode Exit fullscreen mode

As you can see, there are a couple of fields: name and credit card. The credit card field is composed of two "subfields": brand and number. If we try to get the values from the form (for example, using the getValues method), we get a flat object like this:

{ "name": "Luca", "brand": "VISA", "number": "1234567890" } 
Enter fullscreen mode Exit fullscreen mode

Let's consider an even more complex form, for example, one where we have more than one credit card:

Complex form

With some minor changes to the previous code, we would get an object like this:

{ "name": "Luca", "brand1": "VISA", "number1": "1234567890", "brand2": "VISA", "number2": "0987654321" } 
Enter fullscreen mode Exit fullscreen mode

But this object structure is not ideal. A better object should have a shape like this:

{ "name": "Luca", "card": [ { "brand": "VISA", "number": "1234567890" }, { "brand": "VISA", "number": "0987654321" } ] } 
Enter fullscreen mode Exit fullscreen mode

How can we transform the first object to the second one? Let's look at the following form, paying particular attention to the field names:

{ xtype: 'formpanel', title: 'Form', items: [{ name: 'name', label: 'Name', xtype: 'textfield', value: 'Luca' }, { xtype: 'fieldcontainer', label: 'Credit card', items: [{ xtype: 'textfield', name: 'card.0.brand', value: 'VISA' },{ xtype: 'textfield', name: 'card.0.number', flex: 1, value: '1234567890' }] }, { xtype: 'fieldcontainer', label: 'Credit card', items: [{ xtype: 'textfield', name: 'card.1.brand', value: 'VISA' },{ xtype: 'textfield', name: 'card.1.number', flex: 1, value: '0987654321' }] }], buttons: [{ text: 'Submit', handler: ... }] } 
Enter fullscreen mode Exit fullscreen mode

The object we get from this form looks like this:

{ "name": "Luca", "card.0.brand": "VISA", "card.0.number": "1234567890", "card.1.brand": "VISA", "card.1.number": "0987654321" } 
Enter fullscreen mode Exit fullscreen mode

This is still not ideal, but by following this "dot notation" convention, we can easily transform this object into the structured version we want.

Converting Dot Notation to Nested Objects

The key to solving this problem is to create a utility function that can parse field names with dot notation and transform them into a nested object structure. The function below, that I call nestify (suggestion are welcome), recursively processes the form values and builds the desired nested JSON structure.

/** * This procedure transforms objects by splitting properties that have * a dot (.) in their name into sub-properties. For example, an object like this: * { * test: 'value', * 'group.first': 1, * 'group.second': 'other', * } * will be transformed as follows: * { * test: 'value', * group: { * first: 1, * second: 'other', * } * } * If the property name contains a number, an array will be created: * { * test: 'value', * 'group.1': 1, * 'group.2': 'other', * } * will be transformed as follows: * { * test: 'value', * group: [1, 'other'] * } * @param {*} values The object to process */ function nestify(values) { // This function takes a single property and breaks it into sub-objects function encodeKeyValues(values, key, keysList, value) { if (keysList.length < 1) { throw new Error('nestify: wrong property name'); } // If the property has no sub-components, assign the value if (keysList.length === 1) { // If the value is an object, decompose it recursively if (Ext.isObject(value)) { encodeValue(value); } if (Ext.isArray(values)) { // If values is an array, append the value if (value) { values.push(value); } } else { // Otherwise create a new property values[keysList[0]] = value; } return; } // If the property doesn't exist, create it if (!values[keysList[0]]) { if (Ext.isNumeric(keysList[1])) { // Create an array if the sub-property name is numeric (e.g., "prop.1") values[keysList[0]] = []; } else { // Otherwise create an object values[keysList[0]] = {}; } } // If there are multiple levels (e.g., 'prop.subprop.innerprop'), call itself recursively encodeKeyValues(values[keysList[0]], key, keysList.splice(1), value); delete values[key]; } // This function processes all properties of "values" and passes them // to the encodeKeyValues function function encodeValue(values) { Ext.Object.each(values, (key, value) => { const keysList = key.split('.'); encodeKeyValues(values, key, keysList, value); }); } encodeValue(values); return values; } 
Enter fullscreen mode Exit fullscreen mode

Conclusion

I think the dot notation in field names provides a clear and intuitive way to define the desired output structure directly in your form definition.

This approach is particularly useful for complex forms where you need to collect hierarchical data or arrays of objects. It eliminates the need for manual post-processing of form data before sending it to your backend services, making your code cleaner and more maintainable.

Top comments (0)