Template promises
Aurelia 2 significantly simplifies the handling of Promises directly within your templates. Unlike previous versions where promise resolution typically occurred in the view model, Aurelia 2 empowers you to manage asynchronous operations directly in the view.
This is accomplished through the promise.bind
template controller. It intelligently manages the different states of a Promise: pending
, resolved
(then
), and rejected
(catch
). This approach reduces boilerplate code and makes asynchronous data handling in templates more declarative and intuitive.
Basic Usage
The promise.bind
attribute allows you to bind a Promise to a template, rendering different content based on the Promise's current state.
In this example:
promise.bind="myPromise"
: Binds thediv
to the Promise namedmyPromise
in your view model.<template pending>
: Content rendered whilemyPromise
is in the pending state (still resolving).<template then="data">
: Content rendered whenmyPromise
resolves successfully. The resolved value is available asdata
within this template.<template catch="error">
: Content rendered ifmyPromise
rejects. The rejection reason (typically an Error object) is available aserror
.
Simple Example with Different Promise States
Let's illustrate with a view model that manages different promise scenarios:
In this example, promise1
is set to resolve after 2 seconds, and promise2
is set to reject after 3 seconds. The template dynamically updates to reflect each promise's state. Notice in promise2
's then
template, we don't specify a variable, indicating we only care about the resolved state, not the resolved value itself.
Promise Binding with Functions and Parameters
You can directly bind a function call to promise.bind
. Aurelia is smart enough to re-invoke the function only when its parameters change, treating function calls in templates as pure operations.
The following example fetches a random advice slip from an API each time a button is clicked:
Key Points:
adviceIndex
: This variable, initialized withlet adviceIndex.bind="0"
, acts as a parameter tofetchAdvice
. IncrementingadviceIndex
via the button click triggers Aurelia to re-evaluatefetchAdvice(adviceIndex)
.Function Re-execution: Aurelia re-executes
fetchAdvice
only whenadviceIndex
changes, ensuring efficient handling of function-based promises.Error Handling: The
.catch
template gracefully handles fetch errors, providing user-friendly feedback and a "Try Again" button.
Isolated Promise Binding Scope
The promise.bind
template controller creates its own isolated scope. This is crucial to prevent naming conflicts and unintended modification of the parent view model or scope.
In this example:
userData
anduserError
: These variables are scoped only within thepromise.bind
context. They do not pollute the parent view model scope.Component Communication: To pass data to child components (like
<user-profile>
), use property binding (e.g.,user-data.bind="userData"
).Parent Scope Access (Discouraged): While you can access the parent scope using
$parent
, it's generally better to manage data flow through explicit bindings and avoid relying on parent scope access for maintainability.
Nested Promise Bindings
Aurelia 2 supports nesting promise.bind
controllers to handle scenarios where one asynchronous operation depends on the result of another.
Flow of Execution:
initialFetchPromise
: The outerpromise.bind
starts withinitialFetchPromise
.Pending State: While
initialFetchPromise
is pending, "Fetching initial data..." is displayed.First
then
(Response): WheninitialFetchPromise
resolves, the resolved value (initialResponse
) becomes available in thethen
template.Nested
promise.bind
(JSON Deserialization): Inside the firstthen
template, a nestedpromise.bind
is used:promise.bind="initialResponse.json()"
. This starts a new promise based on deserializing theinitialResponse
.Nested
then
(JSON Data): WheninitialResponse.json()
resolves, the parsed JSON data (jsonData
) is available in thisthen
template. "Data received and deserialized: ${jsonData.name}" is displayed.Nested
catch
(JSON Error): IfinitialResponse.json()
fails (e.g., invalid JSON), the nestedcatch
template handles the error.Outer
catch
(Fetch Error): IfinitialFetchPromise
initially rejects, the outercatch
template handles the initial fetch error.
Promise Bindings in repeat.for
Loops
repeat.for
LoopsWhen using promise.bind
within a repeat.for
loop, it's crucial to manage scope correctly, especially if you need to access data from each promise iteration. Using let
bindings within the <template promise.bind="...">
is highly recommended to create proper scoping for each iteration.
Importance of <let>
Bindings:
Scoped Context: The lines
<let itemData.bind="null"></let>
and<let itemError.bind="null"></let>
inside thepromise.bind
template are essential. They createitemData
anditemError
properties in the overriding context of eachpromise.bind
iteration.Preventing Overwriting: Without these
let
bindings,itemData
anditemError
would be created in the binding context, which is shared across all iterations of therepeat.for
loop. This would lead to data from later iterations overwriting data from earlier ones, resulting in incorrect or unpredictable behavior.Correct Output: With
let
bindings, each iteration of therepeat.for
loop gets its own isolated scope foritemData
anditemError
, ensuring correct rendering for each promise in the list.
Last updated
Was this helpful?