Dynamic Form Drawer
<bk-dynamic-form-drawer></bk-dynamic-form-drawer>
The Dynamic Form Drawer is used to display a drawer containing a form to edit or create items described by the dataSchema
.
Custom behavior can also specified, allowing extra actions to be included in the drawer footer via buttons.
The Dynamic Form Drawer listens to the add-new and selected-data events to become visible and initialize its form.
How to configure
For a basic usage of the Dynamic Form Drawer, providing a data-schema to interpret the structure of the data to handle is sufficient. Several customizations can be applied to the provided data-schema that tune how the data is handled by the component. Particularly, but not limited to, every field supports a set of options specific for forms.
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id_": {
"type": "string",
"formOptions": {
"hidden": true // no input is rendered for _id field, but the Dynamic Form Drawer still holds its value in the internal representation of the form values
}
},
"__STATE__": {
"type": "string",
"default": "PUBLIC",
"enum": [ // enum string fields are rendered as select fields
"PUBLIC",
"DRAFT",
"TRASH"
]
},
"name": {
"type": "string"
},
"price": {
"type": "number"
}
}
}
}
}
The Dynamic Form Drawer can be opened in two different modes:
- insert: submitting the form signals the need for an item creation. This mode is activated upon listening to an add-new event or a custom event specified by the user in the properties like in the example below:
{
"customEvents": {
"nameOfTheEvent": "insert"
}
}
- edit: submitting the form signals the need for an item creation. This mode is activated upon listening to an selected-data event or a custom event specified by the user in the properties like in the example below:
{
"customEvents": {
"nameOfTheEvent": "select"
}
}
If the property customEvents
is specified the component will stop listening to the events add-new and selected-data
Modes
Insert
When the component reacts to the add-new event or to a custom event specified by the user, the drawer opens and the form initializes its fields with values specified in the payload of the event.
In this mode, upon clicking on the submit button of the footer, the Dynamic Form Drawer signals the request to push a new item to a CRUD collection, emitting the event create-data with payload extracted from the state of the form, particularly its values.
A component such as the CRUD Client could pick up on the create-data
event.
If the form contains files, the component emits a create-data-with-file event, which signals the need to upload files to a file storage service on top of pushing the item to a CRUD collection.
A component like the File Manager could listen to this event. Further details on how Back-Kit components can be composed to handle file fields are available in the specific section.
A transactionId
is added to the meta field of the emitted event to handle possible errors.
Edit
When the component reacts to the selected-data event or to a custom event specified by the user, the drawer opens and the form initializes its fields with the values specified in the payload of the event.
By clicking on the submit button, the Dynamic Form Drawer signals the request to update an item in the CRUD collection, emitting the event update-data with payload determined the state of the form, particularly its values
The item to update is identified by its _id
field, which is a predefined field of Mia Platform's CRUD Service collections.
A component such as the CRUD Client could pick up on the update-data
event.
If the form contains files, the component emits a update-data-with-file event, which signals the need to upload files to a file storage service on top of updating the item in the CRUD collection.
A component like the File Manager could listen to this event. Further details on how Back-Kit components can be composed to handle file fields are available in the specific section.
A transactionId
is added to the meta field of the emitted event to handle possible errors.
Dynamic Context
Several properties of the Dynamic Form Drawer allow dynamic configurations.
By default, such properties are parsed with handlebars, injecting the current state of the form as context through key values
, as well as other information.
{
values: Record<string, any>
currentUser: Record<string, any> // information on currently logged user, if available
}
After Submission
Upon submission, the Dynamic Form Drawer signals the need to push or update an item in the collection - depending on the mode it is operating under.
Usually, an HTTP-like client takes care of these operations, like the CRUD Client.
It is often useful to perform follow-up tasks after creation or editing of an item.
Properties onSuccess
and onFail
allow to append extra tasks to be executed after the successful or unsuccessful submission of the form.
It is possible to distinguish actions based on what mode the form is operating under.
Properties onSuccess
and onFail
should be configured following the Back-kit Action infterface.
Form context can be used in onSuccess
and onFail
properties using handlebars
notation, allowing dynamic configurations. Actions specified with onSuccess
and onFail
are parsed with handlebars, injecting the default context, as well as the response of the submission request:
{
currentUser: Record<string, any>
values: Record<string, any>
response: Record<string, any>
}
where values
is the form values and response
contains an object representation of the content of the payload of the success event linked to the form submission request.
An example is available showing how to configure a Dynamic Form Drawer to perform extra tasks upon submission.
Footer Buttons
By deafult, a submit button is added to the drawer footer, which can be used for signaling the need to add or edit an item in CRUD collection, depending on the operating mode.
Extra buttons
Other than the default submit button, extra buttons can be specified to be included in the drawer footer.
These can be configured with property actions
, as shows in an example below.
The actions
property can be structured as an array of objects, where each object configures a button.
Alternatively, it can provide two arrays that are scoped based on the operating mode of the Dynamic Form Drawer.
{
actions: {
insert: [
{
... // insert-mode action 1
},
{
... // insert-mode action 2
}
],
select: [
{
... // update-mode action 1
}
]
}
}
or
{
actions: [
{
... // action 1 (both insert-mode and edit-mode)
},
{
... // action 2 (both insert-mode and edit-mode)
}
]
}
Each entry of actions
supports all properties of the Button component, with the additional property closeAfter
, which controls whether or not the drawer should be closed after the action is performed (defaults to true).
Actions support dynamic configurations and are injected with the default context of the component, which includes the current form values through values
keyword.
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"actions": [
{
"closeAfter": false,
"content": "Book",
"action": {
"type": "http",
"config": {
"url": "/new-book",
"method": "POST",
"body": {
"_id": "{{values._id}}"
}
}
}
}
]
}
}
A Dynamic Form Drawer that were provided with such actions
property will add three buttons to the footer of the drawer, other than the default button for submission:
- a button with
Cancel
label which simply closes the drawer - a button with
Action GET
label which performs a GET request to specified endpoint, and closes the drawer afterwards - a button with
Action POST
label which performs a POST request to specified endpoint with an object representation of current form values as payload, without closing the drawer afterwards
Omitting submit button
It is possible to omit the default submission button by setting property omitSubmit
to true.
Confirmation dialog on save and on close
It is possible to require confirmation before submitting the form or closing the drawer, using the requireConfirm
property.
requireConfirm
accepts a boolean or an object value, and defaults to false
.
It is furthermore possible to scope confirmation request configuration depending on the triggering action, either closing the drawer or submitting the form:
{
"requireConfirm": {
"onSave": ..., // boolean or object configuration
"onClose": ... // boolean or object configuration
}
}
An example is available shows how to configure the Dynamic Form Drawer to require confirmation before saving.
1. Boolean type
If requireConfirm
is set to true, the Dynamic Form Drawer, upon submission or closing, signals that confirmation for an actions is needed with event require-confirm.
A component such as the Confirmation Modal could react to the event.
2. Object type
An object such as:
{
cancelText?: LocalizedText // cancel button text
okText?: LocalizedText // ok button text
content?: LocalizedText // the content text
title?: LocalizedText // the title text
}
can be provided as value to requireConfirm
.
LocalizedText is either a string or an object mapping language acronyms to strings.
{
"content": {
"it": "Verrà creato un nuovo elemento, procedere?",
"en": "A new element will be created, continue?"
}
}
This allows to request customized labels in the confirmation dialog-box.
When structure in this way, the value for property requireConfirm
is appended to the require-confirm event.
If this request is picked up by a component such as the Confirmation Modal, this prompts the user for confirmation via a pop-up dialog-box having the specified labels.
Integrate custom labels
Custom labels can be specified as localized text, controlling drawer title, submit and reset buttons labels.
Such labels can be scoped based on whether the Dynamic Form Drawer is in edit or create mode.
The provided value for customLabels
property is merged with the component default labels.
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"customLabels": {
"create": {
"title": {
"en": "Add new Order",
"it": "Aggiungi nuovo ordine"
}
},
"update": {
"title": {
"en": "Order Details",
"it": "Dettaglio ordine"
}
}
}
}
}
An example is available showing how to configure a Dynamic Form Drawer to show custom labels.
File fields with meta-data
Fields described in the data-schema as having the type object
or array
and format file
are rendered in the form as drag-and-drop fields. These fields enable interaction with uploaded files and allow uploading new files.
However, when such fields include a dataSchema
or items
property, they are presented within the form as a link along with a button.
The link enables the downloading of the files present in the initial values of the form.
Clicking the button triggers the appearance of components such as File Picker Modal or File Picker Drawer, provided they are included in the plugin configuration.
Both the File Picker Modal and File Picker Drawer offer interaction with the uploaded files and the option to set metadata for newly uploaded files. In this context, the dataSchema
and items
properties define the structure of the associated file metadata.
Upon submission, the form initiates a request to push or update data within a CRUD collection and upload new files to a file storage service. This is accomplished by emitting either a create-data-with-file or an update-data-with-file event, which can be intercepted by components like the File Manager.
Working with Views
The Dynamic Form Drawer can be used with data from Mia-Platform CRUD Service views.
Lookups
More in detail information is available with respect to lookup fields from writable views in the dedicated section.
Fields described inside the data-schema as having type object
or array
and format lookup
are rendered respectively as select and multi-select fields in the form.
Options for such fields will be dynamically fetched from the endpoint specified in basePath
property, using the /lookup
route provided by the CRUD service (version 6.9.0 or higher), which returns a list of objects. Each option fetched like this should have at least a label
field, which is used as display value inside the form, and a value
field which is used as unique identifier for such option.
The form stores selected values for lookup fields in their whole (not just label
and value
fields). Extra fields are thus available in submit payload, as well as in form context.
Extra queries can be specified to be applied when fetching options using property lookupQueries
, which maps ids of lookup fields to ;MongoDB
-like queries. Dynamic values are also available in property lookupQueries
, being provided with form context. An example is available.
Conditional Fields
Conditionally hide/disable fields
Property conditionalOptions
allows to specify dynamic conditions for specific extra form-options to be applied.
This allows to dynamically hide, set to readonly, or disable fields based on the value of the fields within the form.
Property conditionalOptions
expects an array of objects with fields:
property
: id of the target field to which extra form-options might be appliedquery
: theMongoDB
-like query to be used against the current form values in order to establish whether or not to apply the extra form-options to the target field. If the query condition is satisfied by the current form values, form-options are applied. Note that dynamic values can be used to compare values of two entries of the form, via the context injected by the form.option
: the form-options value to be dynamically injected to the target field
Updating form fields triggers new evaluation of the conditional options, thus updating the form fields accordingly when necessary.
An example is available.
Conditionally reset fields value
For each field it is possible to specify dynamic validity conditions which depend on other fields of the form using the property conditionalValues
. Fields that do not meet specified conditions have their value reset.
Property conditionalValues
expects an array of objects with fields:
property
: id of the target field of which to check the valuequery
: the MongoDB-like query to be used against current form values in order to establish whether or not to reset the value of the target field. As long as the query condition is satisfied by the current form values, the field value is considered valid. Once this is no longer the case, the field value is reset. Note that dynamic values can be used to compare values of two entries of the form, via the context injected by the form.
Updating form fields triggers new evaluation of the conditional values, thus updating the form fields accordingly when necessary.
Each entry of an array field is singularly matched against the query. Only invalid entries are removed from the array value.
type ConditionalOption: {
property: string
query: Record<string, unknown>
}
{
"conditionalValues": [
{
"property": "city",
"query": {
"city.countryName": {
"$eq": "country" // `city` is an object field with a `countryName` key. If city.countryName is not equal to the value of `country` form field, `city` field is reset.
}
}
},
{
"property": "dishes", // dishes is an array. Each entry of dishes is singularly matched against the query!
"query": {
"calories": {
"$lt": "{{rawObject context.maxCalories}}" // assuming dishes is an array of objects, entries of dishes that have a field `calories` grater than the current value of form field `maxCalories` will be automatically removed from the dishes array. Helper `rawObject` is used to avoid numeric values from being stringified
}
}
}
]
}
For instance, the following form values are valid according to the above configuration:
{
"country": "Italy",
"city": {
"name": "Milano",
"countryName": "Italy"
},
"dishes": [
{"name": "Tomato", "calories": 30},
{"name": "Pudding", "calories": 300},
],
"maxCalories": 400,
}
If the value of field "country" were to be updated to "France", the value of "city" field would be reset, since the first entry of conditionalValues
would be no longer met:
{
"country": "France",
// "city" field is now undefined
"dishes": [
{"name": "Tomato", "calories": 30},
{"name": "Pudding", "calories": 300},
],
"maxCalories": 400,
}
If, furthermore, the value of field "maxCalories" were to be updated to "200", the value of "dishes" field would be updated, resulting in one entry being reset:
{
"country": "France",
"dishes": [
{"name": "Tomato", "calories": 30}
],
"maxCalories": 200,
}
A complete example of configuration for a Dynamic Form Drawer that conditionally reset fields is available.
Each entry of an array field is singularly matched against the query. Only invalid entries are removed from the array value.
Locale
The texts of the Dynamic Form Drawer can be customized through the property customLocale
, which accepts an object shaped like the following:
type Locale = {
create: {
title: LocalizedText
ctaLabel: LocalizedText
unsavedChangesContent: LocalizedText
saveChangesContent: LocalizedText
}
update: {
title: LocalizedText
ctaLabel: LocalizedText
unsavedChangesContent: LocalizedText
saveChangesContent: LocalizedText
},
form: {
validationMessages:{
default: LocalizedText,
required: LocalizedText,
enum: LocalizedText,
whitespace: LocalizedText,
date:{
format: LocalizedText,
parse: LocalizedText,
invalid: LocalizedText
},
types:{
string: LocalizedText,
method: LocalizedText,
array: LocalizedText,
object: LocalizedText,
number: LocalizedText,
date: LocalizedText,
boolean: LocalizedText,
integer: LocalizedText,
float: LocalizedText,
regexp: LocalizedText,
email: LocalizedText,
url: LocalizedText,
hex: LocalizedText,
file: LocalizedText
},
string:{
len: LocalizedText,
min: LocalizedText,
max: LocalizedText,
range: LocalizedText
},
number:{
len: LocalizedText,
min: LocalizedText,
max: LocalizedText,
range: LocalizedText
},
array:{
len: LocalizedText,
min: LocalizedText,
max: LocalizedText,
range: LocalizedText,
unique: LocalizedText
},
pattern:{
mismatch: LocalizedText
}
},
datePicker: {
lang: {
locale: LocalizedText,
placeholder: LocalizedText,
rangePlaceholder: {
start: LocalizedText,
stop: LocalizedText
},
today: LocalizedText,
now: LocalizedText,
backToToday: LocalizedText,
ok: LocalizedText,
clear: LocalizedText,
month: LocalizedText,
year: LocalizedText,
timeSelect: LocalizedText,
dateSelect: LocalizedText,
monthSelect: LocalizedText,
yearSelect: LocalizedText,
decadeSelect: LocalizedText,
monthBeforeYear: 'true' | 'false',
previousMonth: LocalizedText,
nextMonth: LocalizedText,
previousYear: LocalizedText,
nextYear: LocalizedText,
previousDecade: LocalizedText,
nextDecade: LocalizedText,
previousCentury: LocalizedText,
nextCentury: LocalizedText
},
timePickerLocale:{
placeholder: LocalizedText
}
},
filePicker:{
drawerTitle: LocalizedText,
filePickerTitle: LocalizedText,
dragAndDropCaption: LocalizedText,
ctaLabel: LocalizedText
},
objectEditor:{
editorView: LocalizedText,
tableView: LocalizedText
},
editor:{
editorView: LocalizedText,
rawView: LocalizedText
},
htmlEditor: {
preview: LocalizedText,
html: LocalizedText
},
geopoint:{
latitude: LocalizedText,
longitude: LocalizedText,
phLatitude: LocalizedText,
phLongitude: LocalizedText
},
element: LocalizedText,
elements: LocalizedText,
true: LocalizedText,
false: LocalizedText
}
}
where LocalizedText is either a string or an object mapping language acronyms to strings.
Examples
Example: Chaining tasks after submission
Properties onFail
and onSubmit
allow to specify tasks to be executed after the Dynamic Form Drawer is submitted.
A Dynamic Form Drawer instance configured like:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"onSuccess": {
"type": "file-download",
"config": {
"url": "/latest-report"
}
},
"onFail": {
"insert": {
"type": "push",
"config": {
"url": "/insert-error-faq"
}
},
"select": {
"type": "push",
"config": {
"url": "/edit-error-faq"
}
}
}
}
}
downloads a file form "/latest-report" path on successful form submission, while in case of error navigates to either "/insert-error-faq" or "/edit-error-faq", depending on the operaing mode.
onFail
and onSuccess
support dynamic configurations:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"onSuccess": {
"type": "push",
"config": {
"url": "/orders-details/{{response._id}}"
}
}
}
}
The example is a Dynamic Form Drawer that, after successful submission, navigates to a path that depends on the response of the linked operation (data update or create).
Example: Adding extra buttons to footer
A Dynamic Form Drawer that were provided with an actions
property like:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"name": {"type": "string"}
}
},
"actions": {
"insert": [
{
"content": "Get Orders",
"action": {
"type": "http",
"config": {
"url": "/orders",
"method": "GET"
}
}
}
],
"select": [
{
"closeAfter": false,
"content": "Add Order",
"action": {
"type": "http",
"config": {
"url": "/orders/new",
"method": "POST",
"body": "{{rawObject values}}"
}
}
}
]
}
}
}
adds one buttons to the footer of the drawer, other than the default button for submission:
- if the Dynamic From Drawer is operatin under insert mode, an extra button with label
Get Orders
which performs a GET request to specified endpoint is added to the footer. Clicking this button closes the drawer. - if the Dynamic From Drawer is operatin under edit mode, an extra button with label
Add Order
which performs a POST request to specified endpoint with an object representation of current form values as payload. Clicking this button does not close the drawer (closeAfter
is false).
rawObject is a helper keyword that prevents "values" from being stringified in the body of the request.
Example: Require confirmation on save and close
The Dynamic Form Drawer can request confirmation before submitting the form or closing the drawer.
- With the following configuration:closing or submitting triggers the Dynamic Form Drawer to signal the need for user confirmation about an action with a require-confirm event.
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"requireConfirm": true
}
}
With the following configuration:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"requireConfirm": {
"title": {
"it": "Richiesta conferma",
"en": "Confirmation Reqired"
},
"okText": "OK"
}
}
}closing or submitting triggers the Dynamic Form Drawer to signal the need for user confirmation about an action with a require-confirm event, and appends to the this request the specified internationalized labels.
With the following configuration:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"requireConfirm": {
"onSave": {
"content": {
"it": "Verrà creato un nuovo ordine, procedere?",
"en": "A new order will be created, continue?"
},
"okText": "OK"
},
"onClose": true
}
}
}closing or submitting triggers the Dynamic Form Drawer to signal the need for user confirmation about an action with a require-confirm event. Only in case of form submission, the Dynamic Form Drawer appends specified internationalized labels to the request.
Example: Customizing labels
The Dynamic Form Drawer allows to customize its title, CTA and reset buttons labels, also distinguishing between operating mode.
For instance:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"customLabels": {
"create": {
"title": {
"en": "Add new order",
"it": "Aggiungi nuovo ordine"
},
"ctaLabel": {
"en": "Submit",
"it": "Submit Order"
},
"cancelButton": {
"en": "Cancel",
"it": "Cancel"
},
"unsavedChangesContent": {
"en": "Closing now will discard new order, do you want to continue?",
"it": "Chiudendo ora si perderà l'ordine non salvato, procedere?"
},
"saveChangesContent": {
"en": "A new order will be created, continue?",
"it": "Verrà creato un nuovo ordine, procedere?"
}
},
"update": {
"title": {
"en": "Order detail",
"it": "Dettaglio ordine"
},
"ctaLabel": {
"en": "Update Order",
"it": "Salva Ordine"
},
"cancelButton": {
"en": "Cancel",
"it": "Cancel"
},
"unsavedChangesContent": {
"en": "Closing now will discard changes to the order, do you want to continue?",
"it": "Chiudendo ora si perderanno tutte le modifiche non salvate all'ordine, procedere?"
},
"saveChangesContent": {
"en": "The order will be updated, continue?",
"it": "L'ordine sarà modificato, procedere?"
}
}
}
}
}
Note that not all labels need to be specified when configuring property customLabels
, as this is merged with default labels.
For instance:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"}
}
},
"customLabels": {
"title": {
"en": "Order",
"it": "Ordine"
}
}
}
}
The specified title will be applied to the drawer independently of the operating mode.
Example: Working with views
The Dynamic Form Modal should be provided with a value for the basePath
property when it should interact with data coming form writable views.
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"rider": {"type": "object", "format": "lookup"}
}
},
"basePath": "/orders-view"
}
}
- being "rider" an
object
field withlookup
format, is rendered as a select field - options for "rider" select field are dynamically fetched from
/orders-view/lookup/rider
Example: Lookup Queries
A Dynamic Form Drawer configured like the following
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"dishes": { "type": "array", "format": "lookup" },
"maxCalories": { "type": "number" }
}
},
"basePath": "/orders",
"lookupQueries": {
"dishes": {
"calories": {
"$lt": 300
}
}
}
}
}
fetches options for field "dishes" from orders/lookup/dishes
with the additional condition that "calories" field of dishes collection should be lower than 300, expressed in the query parameters of the request.
Dynamic queries are also available, being provided with form context:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"dishes": { "type": "array", "format": "lookup" },
"maxCalories": { "type": "number" }
}
},
"basePath": "/orders",
"lookupQueries": {
"dishes": {
"calories": {
"$lt": "{{rawObject maxCalories}}" // rawObject can be used to prevent numeric values from being stringified
}
}
}
}
}
in this case, form field "maxCalories" is used to dynamically compute the additional query to use when fetching options for "dishes" lookup field.
In the previous example, helper rawObject
is used to avoid numeric values from being stringified
Example: Conditionally disable a field
Let us assume a Dynamic Form Drawer to be configured like the following:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"items": {"type": "array"},
"budget": {"type": "number"},
"totalPrice": {"type": "number"},
"isGift": {"type": "boolean"},
"status": {"type": "string", "enum": ["Available", "OutOfStock"]}
}
},
"conditionalOptions": [
{
"property": "items",
"query": {
"budget": {
"$lt": "{{rawObject values.totalPrice}}"
}
},
"option": {
"disable": true
}
},
{
"property": "isGift",
"query": {
"$or": [
{"status": {"$eq": "OutOfStock"}},
{"budget": {"$lt": "{{rawObject values.totalPrice}}"}}
]
},
"option": {
"hidden": true
}
}
]
}
}
In this instance, helper rawObject
is used to avoid numeric values from being stringified
Notice how dynamic configurations can be used in defining queries, which allow to compare values of two entries of the form.
In this case, the budget
field is compared to the totalPrice
one.
Using such configuration, field "items" is disabled once field "budget" is lower than field "totalPrice":
{
"items": ["fork", "spoon", "napkins"],
"totalPrice": 15,
"budget": 7
}
Once "budget" field is updated to "20", field "items" is no longer disabled.
Example: Conditionally reset a field
Let us assume a Dynamic Form Drawer to be configured like the following:
{
"tag": "bk-dynamic-form-drawer",
"properties": {
"dataSchema": {
"type": "object",
"properties": {
"city": {
"type": "object",
"dataSchema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"countryName": {"type": "string"}
}
}
},
"country": {"type": "string"},
"dishes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"calories": {"type": "number"}
}
}
},
"maxCalories": {"type": "number"}
}
},
"conditionalValues": [
{
"property": "city",
"query": {
"city.countryName": {
"$eq": "country"
}
}
},
{
"property": "dishes",
"query": {
"calories": {
"$lt": "{{rawObject context.maxCalories}}"
}
}
}
]
}
}
conditionalValues
sets the following conditions:
city
is an object field with acountryName
key. Ifcity.countryName
is not equal to the value ofcountry
form field,city
field is resetdishes
is an array of object field. Each entry ofdishes
is singularly matched against the specified query. Entries of dishes that have a fieldcalories
grater than the current value of the form fieldmaxCalories
are automatically removed from thedishes
array.
In this instance, helper rawObject
is used to avoid numeric values from being stringified
Then, the following form values are valid according to the above configuration:
{
"country": "Italy",
"city": {
"name": "Milano",
"countryName": "Italy"
},
"dishes": [
{"name": "Tomato", "calories": 30},
{"name": "Pudding", "calories": 300},
],
"maxCalories": 400,
}
If the value of field "country" were to be updated to "France", the value of "city" field would be reset, since the first entry of conditionalValues
would be no longer met:
{
"country": "France",
// "city" field is now undefined
"dishes": [
{"name": "Tomato", "calories": 30},
{"name": "Pudding", "calories": 300},
],
"maxCalories": 400,
}
If, furthermore, the value of field "maxCalories" were to be updated to "200", the value of "dishes" field would be updated, resulting in one entry being reset:
{
"country": "France",
"dishes": [
{"name": "Tomato", "calories": 30}
],
"maxCalories": 200,
}
Note how each entry of array fields is singularly matched against the query. Only invalid entries are removed from the array value.
API
Properties & Attributes
Property | Attribute | Type | Default | Description |
---|---|---|---|---|
rootElementSelector | root-element-selector | string | - | Selector to specify where the container should be appended |
dataSchema | - | ExtendedJSONSchema7Definition | - | Data schema describing the fields of the collection to filter |
readonlyOnView | read-only-on-view | boolean | false | Upon marking this prop as true, on selecting a record, the form will be displayed as readonly, with no possibility to edit |
editorHeight | editor-height | string | number | - | Height of the object/array editor field |
allowNavigation | allow-navigation | boolean | 'show-editor' | true | When true, objects and arrays are displayed as a clickable label which allows navigating to nested objects and arrays if a dataSchema is specified. When 'show-editor', the navigation is allowed, and the object/array fields are displayed in a JSON editor. When false, the navigation is not allowed, and the object/array fields are displayed in a JSON editor |
width | - | string | number | - | Width of the drawer |
omitSubmit | omit-submit | boolean | false | Whether or not to include the default submit button |
actions | - | ButtonWithClose[] | {insert: ButtonWithClose[]; select: ButtonWithClose[]} | - | Actions added as buttons to the footer |
liveSearchItemsLimit | live-search-items-limit | number | 10 | Max items to fetch on regex live search |
customLabels | - | CustomLabels | {insert: CustomLabels, update: CustomLabels} | - | Custom localized texts shown as title and CTA button label |
requireConfirm | - | boolean | RequireConfirmOpts | {onSave: RequireConfirmOpts, onSave: RequireConfirmOpts} | false | Whether or not the component should request confirmation before closing and/or before saving |
onSuccess | - | Action[] | {insert: Action[], update: Action[]} | - | Action executed after successful submit |
onFail | - | Action[] | {insert: Action[], update: Action[]} | - | Action executed after failing submit |
lookupQueries | - | LookupQueries | - | Extra queries when fetching options for lookup fields in views |
conditionalOptions | - | ConditionalOption[] | - | Allows specifying dynamic conditions for form-options (hidden / disabled / readonly) to be applied |
conditionalValues | - | Condition[] | - | Allows specifying dynamic conditions for resetting field |
fileFieldsPreview | file-fields-preview | boolean | - | Enables preview of uploaded files in drag-n-drop file fields |
enableSubmitOnFormUntouched | enable-submit-on-form-untouched | boolean | - | Allows submitting an unedited form |
basePath | - | string | - | The URL base path to which to send HTTP requests, used when fetching options for lookup field in views |
customEvents | - | CustomEvents | - | A user-specified custom event that the component listens to |
ButtonWithClose
type ButtonWithClose = Partial<BkButton> & {
closeAfter?: boolean
}
where BkButton references the properties of the Button component.
RequireConfirmOpts
type RequireConfirmOpts = boolean | {
cancelText?: LocalizedText
okText?: LocalizedText
content?: LocalizedText
title?: LocalizedText
}
where LocalizedText is either a string or an object mapping language acronyms to strings.
CustomLabels
type CustomLabels = {
title?: LocalizedText
ctaLabel?: LocalizedText
saveChangesContent?: LocalizedText
unsavedChangesContent?: LocalizedText
}
where LocalizedText is either a string or an object mapping language acronyms to strings.
LookupQueries
type LookupQueries = {
[property: string]: Record<string, unknown> | Record<string, unknown>[]
}
ConditionalOption
type ConditionalOption = {
property: string
query: Record<string, unknown>
option: RHDOptions
}
type RHDOptions = {
hidden?: boolean;
hiddenOnUpdate?: boolean;
hiddenOnInsert?: boolean;
} & {
readOnly?: boolean;
readOnlyOnUpdate?: boolean;
readOnlyOnInsert?: boolean;
} & {
disabled?: boolean;
disabledOnUpdate?: boolean;
disabledOnInsert?: boolean;
}
Condition
type Condition = {
property: string
query: Record<string, unknown>
}
CustomEvents
const customEvents: Record<string, FormOpeningEvent>
type FormOpeningEvent = 'insert' | 'select'
Listens to
event | action |
---|---|
add-new | opens the drawer to create a new item, potentially applying default fields from data schema or data provided in the payload of the event |
selected-data | opens the drawer to edit a selected item, filling in its fields from the data provided in the payload of the event |
nested-navigation-state/push | updates internal representation of the current navigation path by adding one step |
nested-navigation-state/back | updates internal representation of the current navigation path by removing the specified number of steps |
nested-navigation-state/display | updates internal representation of the current navigation and closes the drawer |
success | notifies correct data update as a result of form submission. Payload holds the response of the associated HTTP call and is accessible by action in onSuccess property via response keyword. |
error | notifies that something went wrong during form submission. Payload holds the response of the associated HTTP call and is accessible by action in onFail property via response keyword. |
Emits
event | description |
---|---|
configurable event | properties such as onFail , onSuccess or actions allow to emit custom events |
require-confirm | triggered when trying to close the modal with unsaved data. requireConfirm property allows to customize this behavior |
create-data | requests data creation |
update-data | requests data update |
create-data-with-file | requests data creation and file upload |
update-data-with-file | requests data update and file upload |