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 |
---|---|
|
|
INTEGER |
|
DOUBLE |
|
LONG |
|
DATE |
|
FRAME |
|
|
|
|
|
JSON |
|
COMPOUND |
|
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"
}
}
}
]