James Lock
James Lock

Reputation: 185

Ember JS - how to wait for async function to respond before render

I am currently working on a project that was given to the company via an out-sourced medium. My question is in regards to rendering an image src.

Previously they served the images from the filesystem. We no longer want to use that approach as we now incorporate AWS S3 bucket serving. We also use cloudfront caching so we want to be able to send a URL from the backend to the frontend (which we do) and then render the image.

In the model, I have a function which retrieves the image URL from the backend and returns it, however during the render the image src is being set to [object Object] I think this is setting the src to the function rather than the response.

Here is the function where we render retrieve the url

baseUrl: Ember.computed(async function () {
let adapter = this.store.adapterFor('article-image');
let ajax = this.get('ajax');

let path = ''

let API_URL = `${adapter.buildURL('article-image', this.id)}/original/${this.get('fileName')}`;
let promise = new Promise((resolve, reject) => {
  ajax.request(API_URL, {
    method: 'GET'
  })
    .then(function (response) {
      resolve(response.path);
    })
    .catch(function (err) {
      console.log('error');
      console.log(err);
    });
})
path = await promise
return path;
}),

Here is the where we call the image.baseUrl, where the src is being set to [object Object]

{{lazy-image
url=(concat image.baseUrl)
alt=(if title title (if image.title image.title 'Image'))
class=(if class class 'img-responsive')}}

Upvotes: 2

Views: 3687

Answers (1)

Gaurav
Gaurav

Reputation: 12796

Here, you are returning a promise from your computed property, which is an object, not a string.

Generally, we do not actually wait for the promise to resolve before rendering your component. Instead, we render something (like a loading gif) first and then rerender the image when the promise is resolved.

There are two traditional solutions to this problem. One is to use a PromiseProxy in your computed property.

return Ember.ObjectProxy.extend(Ember.PromiseProxyMixin).create({ promise });

You can use code like this in your template:

{{#if image.baseUrl.isPending}}
  <img src="loading.gif">
{{else}}
  {{lazy-image url=(concat image.baseUrl.content) ...}}
{{/if}}

See https://api.emberjs.com/ember/release/classes/PromiseProxyMixin

The second traditional solution is to use ember-concurrency and avoid computed properties that return promises. This involves rewriting your code to be more imperative.

See http://ember-concurrency.com/docs/introduction/

I generally prefer this solution because working with promise proxies can get complicated and more difficult to understand.

Also, if you were using a more modern version of Ember, (2.10 or later), you could try ember-lifeline, which is much less code to add to your project than ember-concurrency, along with a simpler api that is adequate for this need. (I'm assuming based on your code sample that you are using an older version of Ember but with modern browsers.) See https://ember-lifeline.github.io/ember-lifeline/

Upvotes: 3

Related Questions