Form Fields

Although Limecraft Flow already has a lot of built-in fields, it is often useful to be able to define your own fields, relevant to your use case.

Form Fields can be used to attach custom metadata properties to Delivery Request (submissions), Collections, and Productions. Form Fields actually consist out of two parts: FormFieldValues and FormFieldDefinitions.

FormFieldsDefinition: Setting up form fields

The structure and presence of form fields can be defined and limited by using the FormFieldsDefinition.

Each time a form field is updated or created, that field is validated against the relevant FormFieldsDefinition.

  • For Productions both the form field values and the definition are stored at the production object itself.

  • For DeliveryRequests, the definition is located at the delivery request. The values are stored at the submission.

Property reference

At the top level, the FormFieldsDefinition contains the properties listed below. Each of these will be explained further below in their own subsection.

Field Name Required Type Description Format

clientSettings

Object

fields

Map of FormFieldDefinition

The list of form fields who are subject to validation/visualisation

layout

Object

name

String

validationRules

ValidationRules

Example FormFieldsDefinition

An example with comments of such a FormFieldsDefinition is:

{
    fields: {
        episode_title: {
            userProperties: { // use for client-side only properties. The backend does nothing with these properties.
                prop1: 'value1',
                prop2: 'value2',
                prop3: 'value3'
            },
            order: 0.5,

            //The type is enforced by trying to cast/convert the values in the custom fields to the type denoted in the CustomFieldsDescription.
            // Available types are: STRING, TEXT, BOOLEAN, INTEGER, DATE, DOUBLE and LONG.
            type: 'STRING',

            //Value to display when the annotation has no value for this custom field
            defaultValue: 'no_episode',

            //Label to be shown in ui
            label: 'episode_title',

            //Description
            description: 'episode title',
        },
        clip_number: {
            userProperties: {
                prop1: 'value1'
            },
            order: 0.1,
            type: 'INTEGER',
            defaultValue: 1,
            label: 'clip_number',
            description: 'clip number'
        },
        author: {
            description: 'Author',
            label: 'Author',
            skipWhiteListValidation: true,
            type: 'STRING',

			//It's possible to define blacklist and whitelist values, if the value isn't in the whitelist the the call will be rejected
			// unless the skipWhiteListValidation parameter is set. Whitelist validation will also be skipped when the array is empty.
            whiteList: [
                'Gimli'
            ],

			//When a value is added to the blacklist it's not possible to set the field to that value.
            blackList: [
                'Legolas'
            ],
        }
    }
}

Fields

The fields Map of a FormFieldsDefinition contains the definitions of the individual fields. It is a map from the name of a field to a FormFieldDefinition. The structure of the latter is given below.

Property Reference

The list of form fields who are subject to validation/visualisation

Field Name Required Type Description Format

allowList

List of object

The list of allowed/possible values. Used in combination with `skipWhiteListValidation`. When `skipWhiteListValidation = true`, the client can use the list as possible values.

autocompleteExtractor

String

Used by Limecraft Flow to accomplish autocomplete.

autocompleteUrl

String

Id of the thesaurus which is used for autocomplete.

blackList

List of string

The list of forbidden values.

clientConfig

Object

compoundDefinition

FormFieldsDefinition

defaultValue

Object

Can be used be the client, empty form fields are not filled in with the default value by the server

description

String

Additional information for the form field

handle

String

A unique handle which can be used to be referenced in the validation rules

hidden

Boolean

Indicator for Limecraft Flow to show the form field or not.

label

String

Label for visualization by the client

multiValued

Boolean

Denotes whether this is a multivalued form field or expects a single value

order

Double

The order in which the form fields need to be displayed

double

readOnly

Boolean

Indicator for Limecraft Flow to enable form field editing.

required

Boolean

This field is required.

requiresId

Boolean

When set, an id is expected for each value in the form fields, typically used in combination with thesaurus. Each entry in the value list will have an id which corresponds to an id in the given thesaurus. The ids itself are not verified.

skipWhiteListValidation

Boolean

Skips the validation of the whiteList.

subtype

String

Determines the model type in case type = FLOW_OBJECT

thesaurus

String

Id of the thesaurus which is used for autocomplete.

type

String

Determines how the field will be indexed and stored

Enum: STRING, BOOLEAN, INTEGER, DATE, DOUBLE, LONG, JSON, TEXT, FRAME, COMPOUND, FLOW_ID, FLOW_OBJECT,

userProperties

Object

whiteList

List of string

The list of allowed/possible values. Used in combination with `skipWhiteListValidation`. When `skipWhiteListValidation = true`, the client can use the list as possible values.

Compound fields

A compound field is a form field with type "COMPOUND" and with a valid compoundDefinition. Compound fields give the possibility to define certain combinations of fields into a record.

As the definition is recursive (compoundDefinition is of type FormFieldsDefinition), this makes for a very powerful and flexible structure.

The example below defines a COMPOUND type with fields name and age. Note that we also made the field multiValued.

{
  "type": "COMPOUND",
  "hidden": false,
  "label": "Record",
  "multiValued": true,
  "readOnly": false,
  "requiresId": true,
  "compoundDefinition": {
    "clientSettings": {},
    "fields": {
      "name": {
        "type": "STRING",
        "label": "Your name"
      },
      "age": {
        "type": "LONG",
        "label": "Age"
      }
    }
  }
}

The values of the example above would look like [{ name: "Luke", age: 30 }, { name: "John", age: 40 }], so an entire table of information can be stored.

Relation to user input fields

The Limecraft Flow UI code will render form fields as user input fields. As not all fields of a user input field fieldConfig are allowed on a FormFieldDefinition, the unsupported keys should be set on the userProperties field.

For example, in the FormFieldDefinition below, label and type indicate to the backend this field should be stored as a "STRING" and has label "Role". In addition, via userProperties, we indicate to the UI that it should be shown as a "SELECT" input (you can interpret this as a subtype of the top-level "STRING" type), with the given selectOptions.

{
  "label": "Role",
  "type": "STRING",
  "userProperties": {
    "type": "SELECT",
    "selectOptions": [
      {
        "label": "Actor",
        "value": "ACTOR"
      },
      {
        "label": "Writer",
        "value": "WRITER"
      },
      {
        "label": "Author",
        "value": "AUTHOR"
      }
    ]
  }
}

The following table lists which userProperties.type values are compatible with which type values. More info on the behaviour of each userProperties.type can be found in the user input fields documentation.

type supported userProperties.type

BOOLEAN

  • BOOLEAN (or unset)

INTEGER

  • INTEGER (or unset)

DOUBLE

  • DOUBLE (or unset)

LONG

  • LONG (or unset)

DATE

  • DATE (or unset)

FRAME

  • FRAME (or unset)

STRING

  • STRING (or unset)

  • COLOR

  • SELECT

  • RADIOLIST

  • EMAIL

TEXT

  • TEXT (or unset)

  • all the types supported by STRING type

JSON

  • JSON (or unset)

COMPOUND

  • COMPOUND (or unset)

Validation

Validation of the form fields occurs at update time, meaning that form fields that do not pass the validation will be rejected and will thus not be stored.

During validation, a number of checks occur:

  • all form field values must have a definition;

  • the type of the value is checked against the defined type;

  • value must not be empty;

  • multiple values for a single-valued field;

  • no valueId when requiresId is set;

  • whiteList/blackList check;

  • validationRules: ValidationRules

ValidationRules

These are evaluated using the validation engine.

The validation rules can run at different stages. It is also possible to set up a validationRule which runs in the UI directly.

The following example defines two fields in and out, and defines a restriction on them so that the value of out should be greater than the value of in.

{
    formFieldsDefinition: {
        "fields": {
            "in": {
                label: 'In',
                type: 'INTEGER',
            },
            "out": {
                label: 'Out',
                type: 'INTEGER',
            }
        },
        "validationRules": {
            "rules": [
                {
                    "description": "Not a valid timecode range",
                    "evaluateClientSide": "Blur",
                    "handle": "out-gt-in",
                    "operator": "AND",
                    "rules": [
                        {
                            "leftSide": {
                                "select": "out.value"
                            },
                            "op": "gt",
                            "rightSide": {
                                "select": "in.value"
                            },
                            "type": "LIMECRAFT_RULE"
                        }
                    ],
                    "showClientSide": false,
                    "validateAt": [
                        "Runtime",
                        "DeliveryRequestSubmissionSubmit"
                    ]
                }
            ]
        }
    }
}
validateAt

When validateAt contains "Runtime", and evaluateClientSide is a valid value, the validation rule will execute on the Limecraft Flow UI client. This is not possible for every LIMECRAFT_RULE. Learn more about client side validationRules.

Other values depend on the context the form field is used in. For example, for a DeliveryRequestSubmission, there is a "DeliveryRequestSubmissionSubmit" value. This allows saving values that are not passing the validation rules, but it won’t allow submitting the DeliveryRequestSubmission with these invalid values.

compound validationRules

Note that validationRules can be defined at different levels. Each COMPOUND form field can again have validationRules defined.

refer to fields using a handle

The handle property of a form field can be set to a unique value (within the form). The validation rules can reference this field using the handle by appending it with a colon in the select.

{
    "leftSide": {
        "select": "myHandle:/value"
    }
}

Using the form field name only works when the other field is at the same level. With handle you can reference fields which are further away in the tree of fields (e.g. a part of another COMPOUND field).

ClientSettings

This is a place to store json data which is not interpreted by the backend but can be used by the client application. This is not used at this time.

Layout

The layout is a JSON definition on how the inputs should be presented and grouped on screen.

See Configure a layout to learn how to write a layout for your fields.

The example below defines a layout for 3 fields. The last field people, illustrates the possibility to pass a layoutConfig down to the compound field.

{
  "type": "basic",
  "elements": [
    {
      "name": "name",
      "type": "field-with-label"
    },
    {
      "name": "isanNumber",
      "type": "field-with-label"
    },
    {
      "name": "people",
      "type": "field-with-label",
      "extraClasses": [
        "flow-flex-col"
      ],
      // you can pass a layoutConfig into the compound field
      // the compound has fields name and age
      "layoutConfig": {
        "type": "basic",
        "extraListClasses": [
          "flow-flex-row vert-gap-10px gap-20px"
        ],
        "elements": [
          {
            "name": "name",
            "type": "field",
            "extraClasses": [
              "flow-flex-col"
            ]
          },
          {
            "name": "age",
            "type": "field",
            "extraClasses": [
              "flow-flex-col"
            ]
          }
        ]
      }
    }
  ],
  // classes applied on the layout element at this level
  "extraClasses": [
    "page-style-1__form",
    "version-40px"
  ],
  // classes applied to the list element containing the children of
  // the layout element at this level
  "extraListClasses": [
    "flow-flex-col vert-gap-20px"
  ]
}

FormFieldValues

We talked a lot about defining the definition of form fields, now it’s time to talk about how to store actual values. Form Field Values are a map of the fields declared in the definition containing the actual value for that field.

For the reader familiar with Custom Fields, Form Fields might look very familiar. The FormFieldValues contain only the actual value(s) though, no label or type is stored here. Also in contrast to Custom Fields, single-valued fields have the value property; multivalued fields have the values property.

Property Reference

Single Valued

If the FormFieldDefinition does not have multiValued=true, a form field value is an object with keys value and optionally valueId.

Field Name Required Type

value

The value of the form field

String, Number, Boolean

valueId

An id corresponding to the value of the field. Currently only used for fields linked to a thesaurus.

String

Multi Valued

If the FormFieldDefinition has multiValued=true, a form field value is an object with keys values and optionally valueIds and handles.

Field Name Required Type

values

The value of the form field

Array

valueIds

An id corresponding to the value of the field. Currently only used for fields linked to a thesaurus.

Array

handles

A locally unique handle. Used for creating pointers to values that do not depend on the array index position.

Array

handles

Handles were introduced for multiValued form field values to avoid pointers to form field values that contains array indices, see also withinTargetPointer.

handles is an optional array which is a sibling to values and (optional) valueIds.

{
    "values": ["John", "Don", "Ron"],
    "valueIds": [1, 2, 3],
    "handles": ["0mjj3OrYyuI", "74HWAn0JEla", "EyuuyVUYUpn"]
}

Its reason of existence is when using pointers to address some form field value, like we do with Partial Reject feature. A pointer like /names/values/1 is dependent on the array index and suffers from array index drift if some name is added or removed at or before index position 1. To make things more robust, we introduced handles and a colon in the pointer indicating special resolving:

/names/values/:b

The :b is interpreted as "the array index where 'b' is at in the sibling handles array".

The handles are set by the client. A handle can only contain the 64 characters that are allowed in base64 (so 65th '=' char is not allowed). By convention we use an 11-char string like "EyuuyVUYUpn".

Examples

String, Text

A single-valued STRING (or TEXT) field’s formFieldValues might look like this

{
  "stringFormField": {
    "value": "myValue"
  },
}

For a multi-valued STRING (or TEXT) field

{
  "multiValuedStringFormField": {
    "values": [
      "myText1",
      "myText2"
    ]
  },
}

String linked to thesaurus

When a STRING field is linked to a thesaurus, the formFieldValues will also contain a valueId property.

{
  "thesaurusFormField": {
    "value": "Lost in translation",
    "valueId": "123456"
  },
}

For a multi-valued STRING field linked to a thesaurus we use valueIds instead.

{
  "multiThesaurusFormField": {
    "values": [
      "Lost in translation"
    ],
    "valueIds": [
      "123456"
    ]
  }
}

Boolean

A single-valued BOOLEAN field’s formFieldValues might look like this

{
  "booleanFormField": {
    "value": true
  },
}

For a multi-valued BOOLEAN field

{
  "multiValuedBooleanFormField": {
    "values": [
      true,
      true
    ]
  },
}

Integer, Long

A single-valued INTEGER (or LONG) field’s formFieldValues might look like this

{
  "integerFormField": {
    "value": 50
  },
}

For a multi-valued INTEGER (or LONG) field

{
  "multiValuedIntegerFormField": {
    "values": [
      50,
      100
    ]
  },
}

Double

A single-valued DOUBLE field’s formFieldValues might look like this

{
  "doubleFormField": {
    "value": 10.0
  },
}

For a multi-valued DOUBLE field

{
  "multiValuedDoubleFormField": {
    "values": [
      10.0,
      20.0
    ]
  },
}

Date

A single-valued DATE field’s formFieldValues might look like this

{
  "dateFormField": {
    "value": 1681476898907
  },
}

For a multi-valued DATE field

{
  "multiValuedDateFormField": {
    "values": [
      1681476898907,
      1681390498955
    ]
  },
}

Frame

A single-valued FRAME field’s formFieldValues might look like this

{
  "frameFormField": {
    "value": 100
  },
}

For a multi-valued FRAME field

{
  "multiValuedFrameFormField": {
    "values": [
      100,
      200
    ]
  },
}

Json

A single-valued JSON field’s formFieldValues might look like this

{
  "jsonFormField": {
    "value": {
      "randomField": 5
    }
  },
}

For a multi-valued JSON field

{
    "multiValuedJsonFormField": {
        "values": [
          {
            "randomField": 5
          },
          {
            "randomField": 5
          }
        ]
    },
}

Compound

A single-valued COMPOUND field’s formFieldValues might look like this

{
  "compoundFormField": {
    "value": {
      "name": {
        "value": "John Doe"
      },
      "age": {
        "value": 37
      }
    }
  },
}

For a multi-valued COMPOUND field

{
  "multiValuedCompoundFormField": {
    "values": [
      {
        "name": {
          "value": "John Doe"
        },
        "age": {
          "value": 37
        }
      },
      {
        "name": {
          "value": "Francis Ford Copolla"
        },
        "age": {
          "value": 21
        }
      }
    ]
  },
}

Setting the value of a form field

Once the FormFieldsDescription is set up, you can start saving values to them.

DeliveryRequestSubmission

Setting form field values is done by updating the DeliveryRequestSubmission object. To update the form field of a DeliveryRequestSubmission, you can use a PATCH call as shown below.

PATCH /api/production/7238/delivery_request_submission/3085614

[
    {
        "op": "add",
        "path": "/formFieldValues",
        "value": {
            "name": {
                "value": "001. Aflevering 1"
            },
            "isanNumber": {
                "value": "test"
            }
        }
    }
]

Production

For setting the value of a formField on a production, the API call should go to

PATCH /api/production/7238

The body sent to it will be similar as for setting form field values on a DeliveryRequestSubmission, as explained above.