Getting formData that includes empty form elements

Hi all, is there any config setting that would cause formData to include empty form elements? For example, if a text field “foo” is empty, instead of being omitted from formData, it would have:

“foo”: “”

Hi @brockfanning,

There is no such option. Currently this is controlled via the renderers. For example if a user deletes all characters of a string input, then we trigger an update of that property with undefined as the value.

You could use custom renderers, wrapping the existing ones and hand over your own handleChange which places the empty string instead of undefined in the data.

Alternatively you could use a middleware. In it you could handle the update actions differently, by placing an empty string in the data instead.

Thanks very much @sdirix!

Just to follow up for posterity - in my case the prospect of overriding a lot of renderers seemed daunting, so I went with the middleware approach. Here’s an example of what I came up with. In this case, I am only concerned with strings, integers, booleans, and arrays - but if I had to deal with other element types then it would need to be extended. This seems to be working OK so far.

import React, { useState, useCallback } from 'react';
import { UPDATE_DATA, INIT } from '@jsonforms/core';
import { get, set } from 'lodash';

[more stuff snipped, skipping to the middleware part...]

    const middleware = useCallback((state, action, defaultReducer) => {
        const newState = defaultReducer(state, action);
        if (action.type === UPDATE_DATA) {
            const schemaPath = 'properties/' + action.path
                .split('.')
                .join('/properties/');
            const pathParts = schemaPath.split('/');
            const property = get(state.schema, pathParts);
            const newData = get(newState.data, action.path);
            if (typeof newData === 'undefined') {
                switch(property.type) {
                    case 'array':
                        set(newState.data, action.path, []);
                    case 'boolean':
                        set(newState.data, action.path, false);
                    default:
                        set(newState.data, action.path, '');
                }
            }
        }
        return newState;
    });

Using the action.path to resolve the schema part might be a bit unreliable. Instead you could resolve the data before the action is applied. Then check the data again after the action is applied. If it’s undefined afterwards, you could check the before-data to determine the data type.

In general it would not hurt if we would provide more context data in all actions for easier customization. That would be a nice enhancement for the future.

Thanks for that idea @sdirix. That makes it simpler. Here’s an updated version:

import React, { useState, useCallback } from 'react';
import { UPDATE_DATA, INIT } from '@jsonforms/core';
import { get, set } from 'lodash';

[more stuff snipped, skipping to the middleware part...]

    function getTypeOfData(data) {
        return (data instanceof Array) ? 'array' : typeof data;
    }

    const middleware = useCallback((state, action, defaultReducer) => {
        if (action.type === UPDATE_DATA) {
            const typeBefore = getTypeOfData(get(state.data, action.path));
            const newState = defaultReducer(state, action);
            const typeAfter = getTypeOfData(get(newState.data, action.path));
            if (typeAfter === 'undefined') {
                switch(typeBefore) {
                    case 'array':
                        set(newState.data, action.path, []);
                    default:
                        set(newState.data, action.path, '');
                }
            }
            return newState;
        }
        else {
            const newState = defaultReducer(state, action);
            return newState;
        }
    });

Looks good, just make sure that if you also have booleans, numbers and objects, to also handle them :wink: