A collection of components and helpers for handling async data in Ember.
ember install ember-async
The async-view
component is a higher-order component providing support for
deferred value resolution using promises, rendering different views for the
different promise states.
When passed a value, if the value is a promise, a pending view is rendered until the promise settles. If the promise fulfills successfully, then this component yields to its block expression; if the promise is rejected, an error view is displayed. If the value passed is not a promise, it is rendered immediately by yielding to its block expression.
When yielding to the block expression, both the fulfilledValue
and state
are
passed; when yielding to a pending or rejected component, the original value
and state
are passed (thus allowing the reason
to be retrieved from a
rejected promise).
Using this component, the fulfilledValue
is guaranteed to be the resolved
value from the promise, or be undefined, thus allowing components that require
an actual model instance (such as ember-form-for
when getting a localized
string) to work correctly.
Note: This component has been designed for values that implement Ember.PromiseProxyMixin, such as models returned from a store. Regular promises are not yet supported.
{{#async-view model as |loadedModel|}}
{{my-form loadedModel}}
{{#/async-view}}
{{#async-view model
pendingMessage="Fetching..."
rejectedMessage="Oops! Something went wrong"
as |loadedModel|}}
{{my-form loadedModel}}
{{#/async-view}}
{{#async-view model
pendingComponent=(component "spinner")
rejectedComponent=(component "error-message")
as |loadedModel|}}
{{my-form loadedModel}}
{{#/async-view}}
If using async-view
throughout an application, it can be convenient to specify
configuration information globally. This can be done through the environment
under the key async-view
within the ember-async
section.
Use the component in a template the same as with the default configuration:
{{#async-view model as |loadedModel|}}
{{my-form loadedModel}}
{{#/async-view}}
Then specify your options in the app configuration:
ENV['ember-async'] = {
'async-view': {
'pendingComponent': 'spinner',
'rejectedMessage': 'Oops! An error occurred'
}
}
If you specify any parameters in a template, they will override the parameters specified in the configuration.
The value to resolve.
The class name to use on the element wrapping the pendingMessage
or
pendingComponent
. Defaults to async-view--pending
.
The message to display while a promise is pending. Ignored if
pendingComponent
is specified. Defaults to Loading...
.
The component to render while a promise is pending. Can be set to either a
component instance, or the name of the component. Passed value
and state
as
positional parameters.
The class name to use on the element wrapping the rejectedMessage
or
rejectedComponent
. Defaults to async-view--rejected
.
The message to display when a promise is rejected. Ignored if
rejectedComponent
is specified. If neither rejectedMessage
or
rejectedComponent
are specified, then the message from the reason for the
rejection will be displayed.
The component to render when a promise is rejected. Can be set to either a
component instance, or the name of the component. Passed value
and state
as
positional parameters.
If value
is a promise, the resolved value of that promise once it is
fulfilled; otherwise, if the promise remains pending or was rejected,
undefined
. If value
is not a promise, then returns value
.
An object representing the state of the promise containing four properties:
isPending
, isSettled
, isFulfilled
and isRejected
.
When a promise is pending or rejected, the message or component is wrapped in a
div
using the class name async-view--pending
or async-view--rejected
,
respectively.
When this component yields to its block expression, it does so without extra DOM elements, so no styling can be applied.
asyncComputed
allows properties that need to be calculated asynchronously to
update templates when they resolve. Using asyncComputed
also enables to you
await on computed properties to get their resolved value.
Use the same syntax with asyncComputed
as you would with computed
, but pass
in an async function:
user: asyncComputed('userId', async function() {
let data = await this.get('store').findRecord('user', this.get('userId'));
// transform data
return data;
})
Or use the get/set syntax with asyncComputed
:
user: asyncComputed('userId', {
async get() {
let data = await this.get('store').findRecord('user', this.get('userId'));
// transform data
return data;
},
async set(key, value) {
let updatedValue = await saveValue(value);
return updatedValue;
}
})
If you need to await on nested async computed properties, or regular computed
properties that are returning promises (e.g. by returning the result of
findRecord
), then use asyncGet
to await each property in the path to ensure
you get the final resolved value.
For instance:
let accounts = await asyncGet(this, 'user.accounts');
is equivalent to:
let user = await get(this, 'user');
let accounts = user ? await get(user, 'accounts') : undefined;
And solves the problem of user
not being awaited on when this is done:
let accounts = await get(this, 'user.accounts');
Provides helpers for handling async data within templates.
A button whose state reflects the state of a promise returned from a closure action, e.g. for updating the text based on the state of saving a form.
© 2017 Crunchy Bananas, LLC
Licensed under the MIT license