Configs

The config property is either an object which has the following possible properties:

  • fields: A function which returns an array of field configurations
  • fieldConfigs: An optional object of config templates
  • asyncDataLoader: An optional function which returns a promise with data loaded from an API

Or it is just the function of the fields property or super simplified, an array of config objects. Depending of the complexity of the form, choose your ideal option.

Fields Configuration

A field configuration looks something like this:

<Form
    config={{
        fields: () => {
            return [
                {
                    id: "email",
                    label: "Email",
                    type: "email",
                    isRequired: true
                },
                {
                    id: "password",
                    label: "Password",
                    type: "password",
                    isRequired: true
                }
            ]
        }
    }}
    ...
/>

It's a function because it can be dynamic, based on data you've entered:

<Form
    config={{
        fields: (data) => {
            return [
                {
                    id: "email",
                    label: "Email",
                    type: "email",
                    isRequired: !data.isLoggedIn
                },
                {
                    id: "password",
                    label: "Password",
                    type: "password",
                    isRequired: true
                }
            ]
        }
    }}
    ...
/>

Each object in the array needs at least this two properties:

  • id: This defines how the property in the data object is named
  • type: This is the key of the field type. The field type needs to exist in the fields object, otherwise nothing is rendered.

For accessiibility reasons, you normally have at least another required property, label, which adds a label to the field.

Other often used properties:

  • value: You can set the value directly here. For computed value, use the computedValue prop
  • options: An array with options for a select or a radio group, usually an object with value and text props
  • placeholder: A placeholder for input fields (pro tip: If you add an array of placeholders, Stages will choose of of them randomly!)
  • isRequired: Is the field required? This will be automatically validated for you by the Form component
  • isDisabled: Is the field disabled? If yes, set it with this property. Normally used for fields with computed values
  • prefix: A text which is show right before the field, in the same line
  • suffix: A text which is show right after the field, in the same line
  • secondaryText: A descriptive text usually shown right below the label

Additionally, Stages comes with a few special properties which are field independent:

  • errorRenderer: A custom error renderer, to make errors look nice
  • defaultValue: Initially sets a default value, except the field is already set by the forms initial data. This can be a function: data => { return "Something ..." }
  • computedValue: You can compute a value dynamically based on the current form data
  • filter: You can filter all user input with a filter function
  • clearFields: This is an array of field keys to clear if this field changes. Optionally you can add a function here which dynamically returns an array of fields to clear: (value, data, errors) => { return ["some", "array", "of", "field", "keys"] }
  • cleanUp: Add a callback here if you want to clean up the value on blur, something like: value => value.trim()
  • isInterfaceState: Set this to true and the field data will not show up outside of the form, but will show up in fieldProps so you can use it in your render function. Great to have UI elements like switches or tabs in your form, but you don't want to expose that UI state to the outside world.
  • precision: For number fields like prices. Set this to 2 and all values will be rendered like the following examples: 2.00, 13.76, 6.10

See the detailed info on those in the respective doc pages.

Config Templates

Sometimes you want to reuse configurations at different places in your data structure. To do that, you can use config templates. An example:

const config = {
    fieldConfigs: {
        coordinates: (data) => {
            return {
                id: "coords",
                type: "group",
                fields: [
                    {
                        id: "lat",
                        label: "Latitude",
                        type: "text"
                    },
                    {
                        id: "lng",
                        label: "Longitude",
                        type: "text"
                    }
                ]
            };
        }
    }, 
    fields: (data) => {
        return [
            {
                id: "field",
                label: "Field",
                type: "text"
            },
            "coordinates",
            {
                id: "mygroup",
                type: "group",
                fields: [
                    {
                        id: "subfield",
                        label: "Sub Field",
                        type: "text"
                    },
                    "coordinates"
                ]
            }
        ];
    }
};

This will create the following data structure:

{
    field: "Bla ...",
    coords: {
        lat: "3.687",
        lng: "34.988"
    },
    mygroup: {
        subfield: "Bla, bla ...",
        coords: {
            lat: "4.376",
            lng: "23.976"
        }
    }
}

Another use for config templates, is to dynamically modify configs from the render function. An example:

render: = ({ fields, modifyConfig }) => {
    return (
        <div>
            {fields.field}
            <br />
            <fieldset>
                {fields.mygroup.coords ? (
                    <>
                        <fieldset>
                            {fields.mygroup.coords.lng}
                            <br />
                            {fields.mygroup.coords.lat}
                        </fieldset>
                        <br />
                    </>
                ) : null}
                {!fields.coords ? (
                    <button onClick={() => modifyConfig("mygroup", "coordinates", "add")}>
                        Add Coordinate Fields
                    </button>
                ) : null}
                {fields.coords ? (
                    <button onClick={() => modifyConfig("mygroup", "coordinates", "remove")}>
                        Remove Coordinate Fields
                    </button>
                ) : null}
            </fieldset>
        </div>
    )
}

The modifyConfig method has three parameters:

  • path: The data structure path of where to add the configuration
  • template: The key of the config template which should be added
  • action: The action, which can currently be either add or remove

And finally a last use for field configs is to define basic fields first and than add or overwite properties in your config. Here's an example where we first define a field config for a required field which always uppercases each character and than use that template multiple times in the actual config:

config={{
    fieldConfigs: {
        requiredUppercaseInput: () => {
            return {
                id: "input",
                type: "text",
                isRequired: true,
                filter: value => typeof value === "string" ? value.toUpperCase() : value
            };
        }
    },
    fields: () => [
        {
            id: "title1",
            label: "Title 1",
            type: "requiredUppercaseInput"
        },
        {
            id: "title2",
            label: "Title 2",
            type: "requiredUppercaseInput"
        }
    ]
}}

Config Functions

A third way to define config items, is with a function:

<Form
    config={{
        fields: () => {
            return [
                (data, asyncData, interfaceState) => {
                    return {
                        id: "myField",
                        type: "text",
                        label: "My Field"
                    };
                }
            ]
        }
    }}
    ...
/>

This can be used to define field configs externally, than import them in your forms. And it makes sure, that all your configs actually exists, as your build wont compile without them existing, something which isn't guaranteed with config templates.