Button
<bk-button></bk-button>
The Button component renders a generic button that can be configured to execute some action upon clicking.
How to configure
The behavior upon clicking is defined by property action
, which can be configured to perform the following tasks, or a combination of them:
- push events into the event-bus
- perform HTTP requests
- redirect to another page
- upload / download files
- copy data into clipboard
Property action
can be configured following the Back-kit Action interface.
Action
The action
property allows to configure a Back-kit Action that should be implemented by the rendered button upon clicking.
Each action in the component is executed with the following context:
{
pathnameParams: ..., // parameters extracted from pathname part of URL. Requires `urlMask` property to be specified.
searchParams: ..., // parameters extracted from search part of URL. Requires `urlMask` property to be specified.
currentUser: ..., // data relative to the current user.
selectedData: ..., // currently selected data. Requires `bulkButton` property to be true.
selectedParents: ..., // history of parents through which the user has navigated to get to the current view
context: ... // extra context set by mounting component
}
which allows for dynamic configurations through handlebars syntax.
{
"tag": "bk-button",
"properties": {
"content": "Register to race",
"action": {
"type": "http",
"config": {
"url": "/race",
"method": "POST",
"body": {
"id": "{{currentUser.id}}",
"nickname": "{{currentUser.nickname}}"
}
}
}
}
}
Context
Dynamic configuration is possible when defining how the button should react to being clicked. Namely, the entry of the action
property are parsed with handlebars before being converted into callbacks, with the following properties injected as context:
currentUser
, contains information about the current user, for example name and email.pathnameParams
, contains information about the pathname of the current URL. This is only available upon correctly configuring a value for property urlMask.\pathnameParams.params
includes the result of the match between theurlMask
property and the pathname of the URL, whilepathnameParams.path
holds the full pathname.\ It contains the propertyparams
with the keys specified in theurlMask
and the propertypath
with the full query parameters string.{
"params": {
"id": "order-id-1"
},
"path": "/order-details/order-id-1"
}searchParams
contains information about the URL query parameters. This is only available upon correctly configuring a value for property urlMask.\searchParams.params
includes the result of the match between theurlMask
property and the query of the URL, whilesearchParams.path
holds the full query as a string.\ It contains the propertyparams
with the keys specified in theurlMask
and the propertypath
with the full query parameters string.{
"params": {
"sort": "ascending",
"color": "red"
},
"path": "?sort=ascending&color=red"
}selectedData
, contains an array of objects representation of the selected data. Requires the propertybulkButton
to be true, operating the button to operate in bulk mode. Selected data is then accessible in dynamic configuration through keyselectedData
.{
selectedData: [
{
"name": "Sara",
"dateOfBirth": {
"day": "30",
"month": "May",
"year": "1994"
}
},
{
"name": "Dave",
"dateOfBirth": {
"day": "21",
"month": "September",
"year": "1956"
}
}
]
}Most of the times,
selectedData
is used in conjunction with rawObject helper, like{{rawObject selectedData}}
, which signals to the Button that the selected data should not stringified but rather kept as array. For instance, to inject the selected data items into the body of a POST call,{{rawObject selectedData}}
should be used.
selectedParents
, contains an array with all the nesting layers which were navigated.extra context. The Button supports extra context, which could be set by the user using
context
property, or, most of the times, by a parent component that mounts the Button. For instance, the Table component renders a table that may include instances of the Button component as action buttons. The Table provides the mounted Buttons with an object representation of the corresponding row, which can then be used in action configuration via keyworkargs.[1]
.
Require Confirmation
It is possible to ask for confirmation before executing an action emitting a require-confirm event, and nesting the desired action in the payload of the event. This approach requires a component such as the Confirmation Modal to be included in the plugin.
{
"tag": "bk-button",
"properties": {
"action": {
"type": "event",
"config": {
"events": {
"label": "require-confirm", // -> this opens the confirmation dialog-box
"payload": {
"configOk": { // -> this is the OK button of the confirmation dialog-box
"tag": "bk-button",
"properties": {
"content": "Confirm",
"action": {
... // -> action to be executed, for which a confirmation should be requested
}
}
}
}
}
}
}
}
}
When wrapping the action inside a require-confirm
event in this way properties loadingOnAction
and disableOnAction
should not be set to true, as the button will enter loading / disabled status without ever leaving it.
Bulk button
It is possible to create a button that keeps in state selected items by setting bulkButton
property to true.
By setting bulkButton
property to true, the Button keeps an internal representation of items selected through components such as the Table.
Selected data can then be referenced in actions using selectedData
.
Most of the times, selectedData
should be used in configurations with rawObject helper keyword, {{rawObject selectedData}}
.
rawObject
prevents the referenced data from being stringified during the dynamic value resolution step.
{
"tag": "bk-button",
"properties": {
"content": "Register to List",
"bulkButton": true,
"action": {
"type": "http",
"config": {
"url": "/v2/users/",
"method": "POST",
"body": "{{rawObject selectedData}}"
}
}
}
}
Loading / disable on action
Properties loadingOnAction
and disableOnAction
put the button in loading/disabled state when an action of type event
, http
, file-upload
is executed.
The button exits loading/disabled status once any result event is received about the outcome of the action. Result events are: success, error, cancel.
{
"tag": "bk-button",
"properties": {
"loadingOnAction": true,
"action": {
"type": "http",
"config": {
"url": "/orders-count",
"method": "GET"
}
}
}
}
For actions of type http
and file-upload
, result events are emitted by the Button itself. This virtually ensures that the Button exists loading/disabled state,
provided a response is received.
However, for actions of type event
, result events are not emitted by the Button itself, but rather rely on other components to do so - typically client components, like the Crud Client. Generally, clients emit result events after performing HTTP requests.
Consequently, in order to avoid entering loading/disabled status without ever leaving it, a Button that emits events should only use loadingOnAction
or disableOnAction
if a following result event is eventually triggered - that is, if the button sends events that trigger an HTTP call from clients.
For instance, create-data, update-data, delete-data eventually trigger the CRUD client to emit result events, allowing the Button to exit loading/disabled state.
Examples
Example: Basic Usage
The Button can be configured to execute multiple types of Back-kit Actions, and combine them.
Example of a Button that executes as HTTP call:
{
"tag": "bk-button",
"properties": {
"content": "Count Orders",
"loadingOnAction": true,
"action": {
"type": "http",
"config": {
"url": "/orders-count",
"method": "GET"
}
}
}
}
Example of a Button that notifies the need for item creation via event:
{
"tag": "bk-button",
"properties": {
"content": "Create New Item",
"action": {
"type": "event",
"config": {
"events": {
"label": "add-new",
"payload": {}
}
}
}
}
}
Example of a Button that navigates to a given href
:
{
"tag": "bk-button",
"properties": {
"content": "Go To Customers",
"action": {
"type": "href",
"config": {
"href": "/cutomers-list"
}
}
}
}
Example of a Button that performs an HTTP call and, in case of successful response, navigates to a given href
:
{
"tag": "bk-button",
"properties": {
"content": "Count orders and go to customers",
"action": {
"type": "http",
"config": {
"url": "/orders-count",
"method": "GET"
}
},
"hooks": {
"onSuccess": {
"type": "href",
"config": {
"href": "/cutomers-list"
}
}
}
}
}
Example: Use Data from URL
The Button component can use urlMask
property to extract information from the current page URL, and inject it as context when resolving dynamic references from its action configuration.
{
"tag": "bk-button",
"properties": {
"content": "Add to chart",
"urlMask": "/order-details/:id",
"action": {
"type": "http",
"config": {
"url": "/chart",
"method": "POST",
"body": {
"id": "{{pathnameParams.params.id}}"
}
}
}
}
}
The resulting component is a button that, on click, performs a POST request to "/chart", with body containing information extracted from the current page URL and from the information of the currently logged user. Assuming the URL to be "/order-details/order-id-1", then body of the POST request will be:
{
"id": "order-details-1"
}
Pathname vs Query
It is also possible to specify different masks for pathname and query portions of the URL.
{
"tag": "bk-button",
"properties": {
"content": "Add To Chart",
"urlMask": {
"pathname": "order-details/:id",
"search": "\\?color=:myColor"
},
"action": {
"type": "http",
"config": {
"url": "/chart",
"payload": {
"orderId": "{{pathnameParams.params.id}}",
"color": "{{searchParams.params.myColor}}"
}
}
}
}
}
Assuming the URL to be "/order-details/order-id-1?color=red", then body of the POST request will be:
{
"orderId": "order-details-1",
"color": "red"
}
Example: Inside Table
One common use case of the Button component is to be used as action buttons inside other components, such as the Table component.
The Table renders all action buttons in every rows of the table, and injects the object representation of the corresponding row as extra context for the Button, retrievable via the args.[1]
keyword, which can be used when configuring the action via handlebars.
{
"args.[1]": {...}, // Corresponding row of the table
"args.[2]": ..., // Index of the row of the table
}
The following configuration renders a Button component which, on click, notifies the need to visualize / allow editing of the details of the corresponding row. Technically: emits a selected-data event with the corresponding table row as payload.
{
"tag": "bk-button",
"properties": {
"content": "Edit",
"action": {
"type": "event",
"config": {
"events": {
"label": "selected-data",
"payload": "{{rawObject args.[1]}}"
}
}
}
}
}
rawObject is a helper keyword that prevents selected items from being stringified in the payload of the event.
The following button downloads a file from an endpoint that depends on the value of the imageUrl
field of the corresponding table row:
{
"tag": "bk-button",
"properties": {
"content": "Download Image",
"action": {
"type": "file-download",
"config": {
"url": "/files/{{rawObject args.[1].imageUrl}}"
}
}
}
}
Example: Nested objects
The Button provides context to retrieve information on the navigated nested objects through keywork selectedParents
, which can thus be used within handlebars to configure the Button action.
Assuming a collection with three levels of nesting is being displayed in a component that allows navigating nesting objects (like the Table component)
companies
- employees
- projects
{
"companies": {
"type": "array",
"items": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"employees": {
"type": "array",
"items": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"projects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string"},
"description": {"type": "string"}
}
}
}
}
}
}
}
}
}
}
When navigating to the third nesting level (entering "projects"), selectedParents
holds information about both the parent company and the parent employee.
{
"selectedParents.[0]": {...}, // selected company
"selectedParents.[1]": {...}, // selected employee
}
When at the "employees" nesting level, a Button for removing an employee from a company could be configured as:
{
"tag": "bk-button",
"properties": {
"content": "Remove from Company",
"action": {
"type": "http",
"config": {
"method": "DELETE",
"url": "/v2/companies/{{selectedParents.[0]._id}}/employees/{args.[1]._id}"
}
}
}
}
selectedParents.[0]._id
is the _id field of company that was selected in the first nesting step, while args.[1]._id
is how to access the "_id" field of a row in the Table component.
Example: Require confirmation
The following button allows the user to upload a file to "/files" endpoint.
{
"tag": "bk-button",
"properties": {
"content": "Confirm",
"action": {
"type": "file-upload",
"config": {
"url": "/files"
}
}
}
}
to require confirmation for this action, it should be wrapped in the payload of event require-confirm:
{
"tag": "bk-button",
"properties": {
"content": "Upload File",
"action": {
"type": "event",
"config": {
"events": {
"label": "require-confirm", // -> this opens the confirmation dialog-box
"payload": {
"content": "Are you sure you want to upload a new file?", // -> this is the message displayed by the dialog-box
"configOk": { // -> this is the OK button of the confirmation dialog-box: the "main action" delegated to this button
"tag": "bk-button",
"properties": {
"content": "Confirm",
"action": {
"type": "file-upload",
"config": {
"url": "/files"
}
}
}
}
}
}
}
}
}
}
If a component such as the Confirmation Modal is included in the plugin, it will react to the require-confirm event emitted by the button on click, using the configOk
key in the payload to determine what action should be executed upon confirmation.
Example: Bulk operations
When items are selected in components such as the Table or the Gallery, a Button with bulkButton
property set to true will provide access to selected items in its configuration through the selectedData
keywork.
{
"tag": "bk-button",
"properties": {
"content": "Register to List",
"bulkButton": true,
"action": {
"type": "http",
"config": {
"url": "/v2/users/",
"method": "POST",
"body": "{{rawObject selectedData}}",
}
}
}
}
rawObject is a helper keyword that prevents selected items from being stringified in the body of the request.
Example: Deselect items after action
When items are selected in components such as the Table or the Gallery, executing a bulk action from the Button might not trigger items de-selection. It is possible to deselect them by piping a change-query event with an empty payload after the main action, using actions hooks.
{
"tag": "bk-button",
"properties": {
"content": "Register to List",
"bulkButton": true,
"action": {
"type": "http",
"config": {
"url": "/v2/users/",
"method": "POST",
"body": "{{rawObject selectedData}}",
},
"hooks": {
"onFinish": {
"type": "event",
"config": {
"events": {
"label": "change-query",
"payload": {}
}
}
}
}
}
}
}
Example: Enter loading/disabled state during action
{
"tag": "bk-button",
"properties": {
"loadingOnAction": true,
"action": {
"type": "http",
"config": {
"url": "/orders-count",
"method": "GET"
}
}
}
}
Upon clicking the rendered button:
- the Burron enters loading state
- the Burron performs the http POST request
- the Burron emits a success or error event, depending on the result of the call
- the Burron dismisses loading state after listening to the result event emitted by itself in the previous step
Example: Loading with action chaining
A Button can be configured to execute multiple using action hooks. The following example shows a Button that chains two actions on click:
- sends an GET request to "/order-metadata"
- then, once a response is received, emits a create-data event.
Properties loadingOnAction
and disableOnAction
cause the Button to enter loading/disabled state once for each chained action of type events
, http
, file-upload
.
{
{
"tag": "bk-button",
"properties": {
"content": "Count orders and create data",
"loadingOnAction": true,
"action": {
"type": "http",
"config": {
"url": "/order-metadata",
"method": "GET"
},
"hooks": {
"onFinish": {
"type": "event",
"config": {
"events": {
"label": "create-data",
"payload": {
"name": "New Order",
"price": 150
}
}
}
}
}
}
}
}
}
with such configuration, if the CRUD Client component is included in the plugin, the Button enters loading state twice when clicked, once for each chained action. The full flow can be broken down as follows. Upon clicking:
- the Button enters loading state
- the Button performs the http POST request
- the Button emits a success or error event, depending on the result of the call
- the Button dismisses loading state after listening to the result event emitted by itself on step 3
- the Button emits an event with label
create-data
and specified payload, and parallely newly enters loading state - the CRUD Client listens to the
create-data
event, which triggers its own flow to create a new item - the CRUD Client emits a success or error event depending on the result of the item creation
- the Button dismisses loading state after listening to the event emitted by the CRUD Client on step 8
The Button thus enters and dismisses loading state twice.
Please note that, in the case of actions of type http
, the button itself emits a result event (step 3). As a consequence, the button's loading state will be appropriately terminated once a response is received.
On the contrary, actions of type event
prompt a result event to be emitted from a distinct component (step 7). This implies that the button's successful exit from the loading state hinges on the reactions of other components to the event that is emitted. Therefore, when configuring the button's action, extra caution should be exercised.
API
Properties & Attributes
property | attribute | type | default | description |
---|---|---|---|---|
content | - | LocalizedText | {} | button content |
danger | danger | boolean | - | danger flag |
disableOnAction | disable-on-action | boolean | false | configures the button to be disabled while action is in progress |
disabled | disabled | boolean | false | button disabled property |
iconId | icon-id | string | - | defines which icon should be rendered into the button, if this property is not defined or doesn't match any icon no icon will be rendered |
iconPlacement | - | "default" | "left" | "right" | "default" | defines where icon should be rendered, either left or right defaulting on left |
listenToLoadingData | listen-to-loading-data | boolean | false | configures the button to be loading when trigger by a loading-data event |
loading | loading | boolean | false | button loading property |
loadingDebounce | loading-debounce | number | 400 | min time in milliseconds between loading swaps (when less it doesn't trigger loading rendering) |
loadingOnAction | loading-on-action | boolean | false | configures the button to be loading while action is in progress |
navigationStrategy | - | "disable" | "hide" | - | determines the button behavior upon navigating nested objects. Allowed values are 'disable' and 'hide'. By default, the button does not react to navigation events. |
shape | shape | string | 'round' | button shape property |
type | type | string | 'primary' | button type property |
urlMask | url-mask | UrlMask | '' | url mask to apply to the current path to extract dynamic parameters |
action | - | Action | - | schema describing How to configure onClick event |
bulkButton | - | boolean | false | whether to use it as a bulk button or not. If set to true, it listens to selected-data-bulk event |
Listens to
event | description |
---|---|
loading-data | enters loading state if property listenToLoadingData is set to true |
selected-data-bulk | keeps track of user selections if property bulkButton is set to true |
nested-navigation-state/back | keeps track of navigation steps |
nested-navigation-state/push | keeps track of navigation steps |
Emits
event | description |
---|---|
configurable event | generic event configurable through the event type configuration |
error | contains error messages for an http event |
success | notifies a successful http request |