« return to the patterns overview
Resource events are essential to any non-trivial LaxarJS application, as they allow to share application resources and to collaborate on them. Because events are always delivered by copy, there is no danger of widgets and activities performing conflicting operations on shared mutable state.
When using the LaxarJS resource pattern, for a given resource there is usually a single widget (or activity) responsible for providing it, in order for others to collaborate. If applicable, that widget (the resource master) is also capable of persisting modifications to the resource. The other collaborators are slaves with respect to the shared resource.
Only the resource master may change the identity of a shared resource, while the slaves may only trigger modifications to the state of the resource.
As an example, consider a shopping cart widget that displays a list of positions (articles and quantities) in a web shop application. The user may select any of the positions, and a second widget will then show details on the selected-position resource, such as a photo of the corresponding article. The shopping cart widget is the resource master here, because it alone determines which article is currently selected. The details widget is a slave, it can only act with respect to the currently selected article.
The details widget might even allow to modify the quantity of a given article within the cart, causing the shopping cart to remove a position when its quantity reaches zero. However, the slave can never change which position is currently the selected-position .
The identity and initial state of a resource is published through the didReplace event by the resource master. Modifications to a resource may be published through the didUpdate event, by master or slaves.
Event name | Payload Attribute | Type | Description |
---|---|---|---|
didReplace.{resource} |
published by a resource master to define state and identity of a shared resource | ||
resource |
string | the topic through which the resource is shared (used in the payload as well as in the event name) | |
data |
object | the (initial or new) state of the resource | |
didUpdate.{resource} |
published by a resource master or by its slaves to publish modifications to the state of a shared resource | ||
resource |
string | see above | |
patches |
array | A JSON-Patch document (an array representing a sequence of incremental modifications) |
Because modifications (didUpdate) are transmitted incrementally, the resource master may use the patches
attribute of the event payload to persist modifications using an HTTP PATCH request.
To create and apply patches, you require laxar-patterns
into your widget controller and use createPatch
and applyPatch
from its json
API.
In support of a unidirectional data flow we strongly advise to only send updates from the master to the slaves and not the other way around. This ensures that there really is only one source for the most recent state of a resource, and changes to it are encapsulated in just one location. So instead of sending a didUpdate event a slave expresses its wish to induce a modification on a resource via takeActionRequest to the master. The master then can modify the resource accordingly (possibly via server roundtrip) and publish the updated version as a whole via didReplace or partially via didUpdate event.
When sharing resources, keep in mind that resource events (like all LaxarJS events) are cloned for each receiver. This makes it easy to write robust applications, but can lead to inefficiencies if very large resources are published. In some cases, it might be worthwhile to factor out sub-resources relevant to the consumers.
Often the individual widgets sharing a resource know best how to validate their specific user input. However, in the context of persistence or navigation the validity state of the overall resource or of the entire page is of interest.
Widgets and activities concerned with navigation and persistence should not be exposed to the details of resource validation [1]. To separate these concerns, widgets may issue requests to validate a resource which other widgets may respond to. Respondents may choose to validate immediately or asynchronously.
A widget may request validation of a resource using a validateRequest
event.
Collaborators capable of performing validation on this resource respond by publishing a willValidate
event.
After they have performed validation, possibly asynchronously, collaborators publish a didValidate
event.
Event name | Payload Attribute | Type | Description |
---|---|---|---|
validateRequest.{resource} |
published by any widget that requires validation of the given resource | ||
resource |
string | the topic through which the resource is shared (used in the payload as well as in the event name) | |
willValidate.{resource} |
published by a widget that is about to perform validation of the given resource | ||
resource |
string | see above | |
didValidate.{resource} |
published by a widget that has performed validation of the given resource | ||
resource |
string | see above | |
outcome |
string | One of ERROR , WARNING , INFO and SUCCESS |
|
data |
array | A list of validation messages for the application user (see below) |
The didValidate
event contains information on the results of a validation:
-
The outcome determines if validation was successful:
ERROR
: The resource is in an invalid state. If the receiver would normally persist the resource, it will probably not do so.WARNING
: The resource probably contains problems. If the receiver would normally persist the resource, it should probably obtain user confirmation before doing so.INFO
: The resource might contain problems or require user intervention. If the receiver would normally persist the resource, it might obtain confirmation or otherwise inform the user.SUCCESS
: No problems were found by the sender of the event. If the receiver would normally persist the resource, it should proceed as far as the sender is concerned.
-
The validation messages (
data
array) contains additional details for the user. Each message is an object with at least these attributes:htmlMessage
: A validation message for the user (a string), to be interpreted as HTML markuplevel
: The severity of this particular vaidation message (a string), using the outcomes specified abovesortKey
: The message priority (a string) that should be used for sorting messages lexicographically.
[1]: As an exception to this rule, a resource master may perform a (semantic) overall validation after the individual editor widgets had their say.
Usually the resource master is responsible for saving user input where appropriate. This can be achieved by performing a REST call for example, or by putting state into a container resource (as a slave) and requesting for the container to be persisted.
To request saving a resource, widgets may publish a saveRequest
event.
Widgets capable of and configured for persisting the resource respond by publishing a willSave
event.
After they have ensured persistence, usually asynchronously, collaborators publish a didSave
event.
Event name - | Payload Attribute | Type | Description |
---|---|---|---|
saveRequest.{resource} |
published by any widget that requires persisting the given resource | ||
resource |
string | the topic through which the resource is shared (used in the payload as well as in the event name) | |
willSave.{resource} |
published by a widget that is about to persist the given resource | ||
resource |
string | see above | |
didSave.{resource} |
published by a widget after saving the given resource | ||
resource |
string | see above | |
outcome |
string | ERROR or SUCCESS |
The didSave
event contains information on the outcome:
ERROR
: The resource could not be saved. The sender should also publish adidEncounterError
event in this case.SUCCESS
: The resource was saved successfully.
Depending on the use case, widgets might persist resources automatically every time they have been modified. In this case, it is recommended for them to still respond to save requests, for best interoperability.
The saveRequest, willSave and didSave events are mainly useful for CRUD driven frontends. When building a RESTful application, resources are most probably shared frequently with services, and user interactions very often directly induce a server side effect. A dedicated phase for persistence (i.e. saving all relevant resources) is less likely to occur. Instead, consistency should somehow be checked before leaving a page, but how that is achieved depends on the functional use case.