Optimal way to pull API data without the store

We have many situations were we are pulling in a very large amount of data (like for a HeatMap) or data that we do not want to cache in the store. We have implemented a large number of solutions over the years but we have not found a good one that affords us a sense of ease. We are missing out on the error handling that a store call has as well as the concise amount of code. What we are looking for the proper way to use calls like $.getJSON that is reusable and something contained.

Here is an example of a version that we have. We do not like that we need to use a property on the component to mark the promise as having errored.

weather: computed('refreshDataTrigger', function()
{
  const that = this;

  let promise = new Promise(function(resolve, reject)
  {
    resolve($.getJSON(`/api/weatherForecasts/current`));
    reject();
  });

  let promiseObject = DS.PromiseObject.create(
  {
    promise: promise
  });

  promiseObject.then(function(value)
  {
    that.set('isErrorState', false);
    return value;
  }, function(reason)
  {
    that.set('isErrorState', true);
    Log.Log(reason);
    return null;
  });

  return promiseObject;
}),

We wanted to use this with a promise addon to handle the status of the promise like we do with normal store calls.

{{#if (is-fulfilled weather)}} {{#if (is-pending weather)}} {{#if (is-rejected weather)}}

Could we get some guidance?

I would personally advocate using Ember Concurrency tasks instead of computed properties and promises for this. They’re more ergonomic and cancelable, among other things. There are a ton of different ways you could do stuff like this, and it depends on whether or not you’re fetching the data in routes or not (my guess based on the above is you’re not) but there are some great examples in the ember-concurrency documentation.

As another example here’s a little addon I wrote which defines a provider component and some helpers for common store actions (which just return ember concurrency taskInstances, so you could use a similar provider with any old ember concurrency task). In a template it looks like this:

  <NeedsAsync @needs={{this.taskInstance}} as |SomeData|>
    <SomeData.loading>
      loading...
    </SomeData.loading>
    <SomeData.loaded as |value|>
      {{value}}
    </SomeData.loaded>
    <SomeData.error as |error|>
      {{error}}
    </SomeData.error>
  </NeedsAsync>

Anyway I’m not suggesting you use that addon or anything, just wanted to give another example of something that I personally find more ergonomic. YMMV. I’d definitely try using EC tasks instead of generic promises though, and avoid async computed properties if you can.

EDIT: the EC task should be pretty straightforward, and you can use jquery, an ember adapter fetch, etc inside and just return the yielded value.

1 Like

We do gather data from the routes in some situations. We found that since we have components that are used on many different routes that it made sense to let the component build itself and let the route provide attributes to help configure the component.

We will look into those items. Thanks for the feedback.

Are you using EmberData elsewhere in this application? If so, and you are willing to be a bit bleeding edge, you have a great use case for exploring some of the new data APIs that make this nicer :slight_smile:

1 Like

We do use EmberData in other places. There are just some places we do not want the overhead of converting the data to models and being cached in the store when it will be used once on graph (1000s of data points). We are on the current LTS at the moment. Is there a new API function that might fit for this?

:wave: Hi @runspired,

Got any more info on these new API’s? I’m currently using an approach for this that you and I spoke about a good while back involving a custom function on an adapter, that uses this.ajax and using this.store.adapterFor to retrieve said adapter and make the request.

This has worked out well for us, but keen to understand a more modern approach if you have one in mind.

@Justin_Proctor I’m aware this doesn’t answer your question. I’ll post a separate reply with an example of how we do this (as touched on above).