« return to the patterns overview
Action Patterns
In LaxarJS applications, actions allow widgets and activities to give their collaborators the opportunity to react to "something that happened". Usually, "something" is a user-interaction: A confirmation button was clicked, a selection was changed or a backend call was completed on behalf of the user.
Other widgets may respond to arbitrary action requests by listening to their configured topics, and taking an action appropriate for them: They might open a popup, start navigation, perform a HTTP request, ask to validate or save some resources, clear their own resources and so on. When widgets respond to an action request, they can do so asynchronously using the [will/did]-mechanism. This means that actions can have a duration, which is useful for longer running tasks such as performing a search. The duration can in turn be used by yet other widgets, for example to show a progress indicator every time that an action on the page runs for more than, say, 200 milliseconds.
If you are familiar with Qt's signal/slot mechanism, you may think of an action as the named, asynchronous counterpart to an n:m signal/slot connection.
Action Requests and Will/Did-Responses
As with resources, the page configuration determines which widgets share action topics: If a widget offers to publish an action, but no topic was configured for that action, it should not publish an event. Similarly, if a widget offers to respond to some action, but no topic was configured, it should not subscribe to action requests.
The takeActionRequest, willTakeAction and didTakeAction Events
A widget (the action initiator) may request for action to be taken by publishing a takeActionRequest
.
Collaborators (action handlers) capable and configured to perform a corresponding action respond by publishing a willTakeAction
event.
After they have completed performing their action, possibly asynchronously, the collaborators publish a didTakeAction
event.
Event name | Payload Attribute | Type | Description |
---|---|---|---|
takeActionRequest.{action} |
published by any widget to request for some action being taken | ||
action |
string | the topic through which respondents are connected (used in the payload as well as in the event name) | |
anchorDomElement |
string | If applicable: the ID of a DOM element where the action originated | |
willTakeAction.{action} |
published by a widget that is about to perform some action | ||
action |
string | see above | |
didTakeAction.{action}.{outcome} |
published by a widget that has completed its action | ||
action |
string | see above | |
outcome |
string | ERROR or SUCCESS (used in the payload as well as in the event name) |
The anchorDomElement
that can be sent along with the takeActionRequest
is useful to display popover hints right next to the UI element that was activated by the user.
This information exposes implementation details of the sender, so respondents should take care not to modify the sender DOM and not to rely on a specific structure.
The outcome
that is sent with the didTakeAction
response indicated if the (assumed) user intent could be satisfied, because of an error condition that could not be handled (such as a network problem).
If the outcome is ERROR
, the initiator should signal this to the user if appropriate, and the handler should publish a didEncounterError
event with details on the problem.
Although any additional information to a takeActionRequest
or didTakeAction
event payload may not be understood by a random third party widget, it may be part of a unidirectional data flow.
For this paradigm it is important, that for any resource only one source exists, that is allowed to replace this resource completely or update parts of it by means of resource replace and update mechanisms.
Any other widget collaborating on the same resource may change this resource locally, but is not allowed to publish any updates to it directly or even replace it.
Instead such a widget will send a takeActionRequest
event with the changes it would like to see for a certain resource as additional payload, ideally as functional request instead of the changes already incorporated into the resource.
The actual change can then be achieved directly by the activity being the master for the resource or by means of e.g. a REST request.