Note: This text was translated into English using AI. Please excuse any unnatural phrasing.I have created a reusable block that only displays a Popup, and I am using it across multiple screens.
Inside this block, I placed a LoadingButton. When the button is pressed, the following logic is implemented:
However, the behavior is not as expected, and the following issues occur:
Ideally, I expect the button to immediately enter a loading state on click, disable further interaction, and then close the Popup.
What I have tried
Questions
The behavior you’re seeing is expected in ODC with blocks + events: all client logic (including UI updates) is executed as one “transaction” before the screen is re‑rendered, and once you trigger a block event, control moves to the parent flow. That’s why:
Below is how to fix this and what pattern to use.
When you do this inside the block:
IsLoading = True
IsPopupVisible = False
the event immediately transfers execution to the parent action, so steps 1 and 3 never get a chance to be reflected in the UI before the long‑running logic completes. ODC: Button Loading inside block doesn't show spinner when calling a block event
This is the same situation described in the article you referenced: the “loading” logic needs to run where the actual work is being done (i.e., in the parent), not in the publisher block. ODC: Button Loading inside block doesn't show spinner when calling a block event
Use an input parameter (e.g., IsSaving) on the event and manage the loading state in the parent that subscribes to the event. This is the same pattern suggested in the forum solution. ODC: Button Loading inside block doesn't show spinner when calling a block event
IsSaving
In the Block:
IsLoading
LoadingButton.IsLoading
Button.Enabled
IsVisible
So something like:
IsSaving: Boolean
IsVisible: Boolean
ButtonLoading.IsLoading = IsSaving
Button.Enabled = not IsSaving
Visible = IsVisible
OnConfirm
In the Parent Screen:
Have local variables:
Popup_IsVisible: Boolean
Popup_IsSaving: Boolean
Bind block inputs:
IsVisible = Popup_IsVisible
IsSaving = Popup_IsSaving
In the parent’s event handler (subscriber):
Popup_IsSaving = True (Loading spinner appears & button is disabled before any heavy logic runs.)
Popup_IsSaving = True
Execute your long‑running logic:
When finished:
Popup_IsSaving = False
Popup_IsVisible = False
If needed, set Popup_IsSaving = False also in exception handling, like communication errors, so the UI does not remain stuck in loading. Prevent users from spam clicking buttons
This way:
This is effectively the same pattern you use in other “prevent multiple clicks” scenarios (boolean bound to Enabled + LoadingButton), just split between block and parent. Prevent user from clicking too fast on several buttons and links
When you tried:
Button.Enabled = SomeLocalFlag
Container.Visible = SomeFlag
you still had the same problem: the assigns occur in the same client‑side execution cycle as the event trigger. The UI only re‑renders after the entire chain (block + parent event) finishes. So you cannot rely on local block state changing after the event has been triggered and expect an immediate visual update.
That’s why the fix is to:
Depending on UX requirements, you have a few patterns:
Close popup after all processing completes (simplest)
Close immediately, process in background
Global “overlay” to block everything
IsGlobalLoading
IsGlobalLoading = True
IsGlobalLoading = False
Whichever you choose, keep the rule: UI state is handled at the consumer (parent), not inside the reusable block once events are involved. This matches the recommended approach in the referenced ODC thread and is what will make your LoadingButton behave correctly. ODC: Button Loading inside block doesn't show spinner when calling a block event
Thanks, OutSystems Neo! This solution resolved my issue. The key was moving the loading state management to the parent screen instead of the block.