Strange behaviour with isPending, isSettled, isFulfilled when using included relationships in API response


#1

I need to understand once for all why property like

isSettled isPending isFulfilled

are different if I’m including or not the data in my API respone.

I’m asking here this: Ember 2, Show a single loading message when the ids where included in the original response which leads me to this strange behaviour:

If I include in my API responde the data (ex: model.posts) these properties are immediately set to true (and .isPending to false) also if Chrome is still loading the real data (also for the first time!).

And this is a problem because I don’t know if the posts[] are empty or not and I don’t know what I can spy because something like that doesn’t work:

{{#each model.posts}}
  My posts.
{{else}}
  {{#if model.posts.isPending}}
    <div>Loading...</div>
  {{else}}
    <div>Nothing to show.</div>
  {{/if}}
{{/each}}

It’s always “Nothing to show.” until Chrome loads. Because .isPending is immediately false.

Also if I use the length attributes:

{{#if (eq model.posts.length 0)}}

because the starting posts[] array and the empty one is always to length == 0.

If I loads posts differently, async, not sideloaded (but with hundreds HTTP requests, which I don’t want) it works. Ember recognize an isPending

Why this strange behaviour?


#2

It looks like model.posts is a DS.PromiseArray. This is an Ember.ArrayProxy mixed with Ember.PromiseProxyMixin.

I think it’s better to think of the object as a Promise first and array second. As such, you need to invert your template’s logic.

{{#if model.posts.isPending}}
  <div>Loading...</div>
{{else if model.posts.isRejected}}
  <div>Failed...</div>
{{else}}
  {{#each model.posts as |post|}}
    My posts {{post.name}}
  {{else}}
    <div>Nothing to show.</div>
  {{/each}}
{{/if}}

#3

Thanks @lightblade, but this is not working.

The problem is not there.


#4

Can you please paste your model() hook code here.

That gives us a better understanding.


#5

Yes dear @Elisha.Ebenezer. Thanks for your answer.

This is my model():

export default Ember.Route.extend({

  model(params) {
    return this.store.findRecord('category', params.category_id);
  }

});

#6

Hello John,

IMHO, the reported behavior seems to be the “right” behavior and as per design :slight_smile:

By looking at your other related post, it is obvious that you are using a JSON API(ish) backend.

Now, whenever a JSON response document contains included, it is treated as compound document and that all the data in the included section is expected/considered to be complete data, unless you ask for sparse fields.

JSON API specification details out on fetching related records and Compound Documents

With that, in your case, when a compound response for category is received, it already contains all the posts and there is no need to resolve posts.

Hence, you’ll never get to else part of each.

If you really want to have lazy loading, you might want to do away with includes and have the relationship definitions with async : true

HTH


#7

Dear @lightblade, sorry, I’m using your template (with isPending or isSettled or isFulfilled) but now I see a little “blink” betweeen <div>Nothing to show.</div> and <div>Loading...</div>

Maybe model.posts.isPending is already immediately true then false, loading… and then first part of each is Ok.

How can I avoid that “blink” / “flash”?


#8

There are two solutions to this:

  1. Make the Promise somehow synchronously resolve. If the promise is resolved before the run loop flushes, you won’t see the blink.
  2. Make the blink last longer to give users easier time to adjust. Just add a 500ms delay in the promise resolve.

#9

Any example of 1? The 2, I think is not good to add time… generally… No?


#10
DS.PromiseArray.create({
   promise: RSVP.resolve(value) // <-- if value is resolved here, then we have no flashes
})

You can even use {{debugger}} to inspect value that might change.


#11

But I need it in model()?