- 19 May 2022
- 4 Minutes to read
-
DarkLight
-
PDF
Button with a loading indicator
- Updated on 19 May 2022
- 4 Minutes to read
-
DarkLight
-
PDF
In this quick tutorial, we’ll show you how to create a Button that shows an in-place loading Spinner when the user presses on it, then changes back once the loading is completed. While the Button is loading, the user cannot interact with it.
This can be useful with actions that aren’t instant and take a while, e.g. complex backend requests, if you don’t want to show a Spinner that covers the whole page.
Defining the Button action
We start by dragging a Button that will trigger a long action to UI canvas. (If you already have some Button in your app where you’d want to implement this loading indicator, you can always use an existing Button instead.)
Here we use Delay flow function to represent the long action.
Creating the loading indicator
Now that we have the Button that should indicate when an action is underway, we can build the loading indicator. Simply drag a Container under the Button and drag a Spinner inside the Container. This Container will replace the Button during the long action.
It's good practice to name the Container to something descriptive, e.g. Loading Indicator Container.
Under the Style tab in the right-hand properties panel, ensure that the Dimensions and position of the Container match the ones of the Button. Also, set the Container to Justify content vertically to middle, so that our Spinner is aligned correctly to the middle.
Then you can add some Borders, Background color and other styles to your Container depending on how you want the Button to look during the Loading phase. Here we set the Borders (and Spinner) to a gray color and change Border radius to 3px to match the Button.
Showing the Loading indicator
Now we have just a Button with a Container and Spinner under it always visible, which means we have to create the logic to show only either one of them at a time.
First, create a Page variable actionInProgress
and change its type to True/false. Then, open the Advanced properties section of the Button Properties tab and bind the Visible value to a formula:
NOT(pageVars.actionInProgress)
This means the Button will be visible when the value of actionInProgress
is False.
For the Container, you can bind the Visible property directly to actionInProgress
, so that it's shown when actionInProgress
is True.
Thus, switching the value of the actionInProgress
page variable now switches which component is shown.
Lastly, you need to toggle the actionInProgress
page variable to True when your Button is tapped. Remember to toggle the value back to False when the action ends, even if your complex action has more outputs that the Delay we are using here. Be sure to cover potential error paths too!
That’s it! Now your Button indicates the user that the action is underway.
Note: With this same logic, you can replace also other parts of the UI during actions. For example, you can change some “Send message” icon into a Spinner, or just show a Spinner in other part of your page.
Advanced: Loading indicators for repeated Buttons
You have now mastered the basics of indicating a loading state in your app through toggling the Visible property of components. Now it’s time to dig in a little deeper: how to show the loading indicator on the correct Button if the Button is in a repeated list?
Repeated Container
First, add the Button and the Loading Indicator Container created in previous steps into another Container (we'll name it Repeated Container).
Bonus, part 1/2
You can add a Title component to the top of the Repeated Container to make the Repeated Containers look more distinct between each other.
Then, under the properties panel, change the Repeat with property of the Repeated Container to be bound to the data you want to repeat (which needs to be of type List of Objects).
Here, we use mock data through a formula binding:
[{"name": "first", "id": GENERATE_UUID()}, {"name": "second", "id": GENERATE_UUID()}, {"name": "third", "id": GENERATE_UUID()}, {"name": "something", "id": GENERATE_UUID()}, {"name": "else", "id": GENERATE_UUID()}, {"name": "indeed", "id": GENERATE_UUID()}]
Note: Each of the items has to have a unique property for this configuration to work. Here we have the id
property created with GENERATE_UUID() formula function.
Bonus, part 2/2
Bind the Title content to Property of data item in repeat >name
.
Toggling visibilities
Now, instead of page variable actionInProgress
we need a page variable that can sort out the exact actions/buttons that are in progress.
Create a new page variable actionIdsInProgress
and change its value type to List. (Here we could also change List item type to UUID, but that’s not necessary, the default Text type works fine).
We’ll use actionIdsInProgress
to contain theid
properties of repeated data items that have their related Button tapped. To add the related id
to the List, change the first Set page variable in the Button tap event to set actionIdsInProgress
with formula:
WITH_ITEM(pageVars.actionIdsInProgress, current.id)
This sets the actionIdsInProgress
list to be itself plus the pressed Button's id
.
Then, after the action of the Button (the Delay node in this example) completes, change the second Set page variable to set actionIdsInProgress
back to not include the related id
with formula:
WITHOUT_ITEM(pageVars.actionIdsInProgress, current.id)
Lastly, you have to bind the Visible properties of the Button and Loading Indicator Container to the values of the actionIdsInProgress
. Each Button should be visible if the actionIdsInProgress
List doesn’t include their id and invisible if their id is in the List. This can be accomplished with another formula:
!IS_IN_ARRAY(pageVars.actionIdsInProgress, current.id)
For the Loading Indicator Container just remove the !
from the beginning and it will be visible when the id
is in the List.
Done! Now you know how to handle visual changes in repeated components.
You can use this same logic with other types of user interface components when working with repeated data. For example you can create accordions or show/hide fields in the repeated Container depending on the value of a Checkbox.