Ember 2, Show a single loading message when the ids where included in the original response

I use very often in my code:

{{#each model.posts as |post|}}

  <div>post.title</div>

{{else}}

  <div>Loading the posts...</div>

{{/each}}

But sometimes I don’t know if the model.posts is empty or not.

How to show a message instead of loading forever an empty array?

The problem here is that I’m in /category/2 page, so in category.hbs template and posts are “sideloaded” in response, like this:

{
  "data": {
    "id": "1",
    "type": "categories",
    "attributes": {
      "name": "Books"
    },
    "relationships": {
      "posts": {
        "data": [{
          "id": "14",
          "type": "posts"
        }, {
          "id": "15",
          "type": "posts"
        }, {
          "id": "20",
          "type": "posts"
        }]
      }
    }
  },
  "included": [{
    "id": "14",
    "type": "posts",
    "attributes": {
      "commented": true,
      "comments": 10
    }
  }, {
    "id": "15",
    "type": "posts",
    "attributes": {
      "commented": false,
      "comments": 10
    }
  }, {
    "id": "20",
    "type": "posts",
    "attributes": {
      "commented": false,
      "comments": 10
    }
  }]
}

I’m using ember-data and my models are:

category

name: DS.attr('string'),
posts: DS.hasMany('post')

post

commented: DS.attr('string'),
comments: DS.attr('number'),
category: DS.belongsTo('category')

Maybe I will create an ember-twiddle, I’m having problem to simulate my problem with ember-data on ember-twiddle…

Hi,

I think you can solve this problem by using computed property in your controller/component, so your code would look like the following.

{{#if arePostsLoading}}
  <div>Loading the posts...</div>
{{else}}
  {{#unless arePostsEmpty}}
    {{#each model.posts as |post|}}
      <div>post.title</div>
    {{/each}}
  {{else}}
    <div>Empty post...</div>
  {{/unless}}
{{/if}}

Hope this help. :slight_smile:

How to do computedProperties like arePostsLoading?

You could try it using setupController in route since this prevents page-blocking. This should work with yours.

setupController(controller, model) {
  this._super(controller, model);
  
  controller.set('arePostsLoading', true);
  this.store.findAll('post').then((posts) => {
    controller.set('model.posts', posts);
    controller.set('arePostsLoading', false);
  });
},

Ok, thanks. But I already have included my comments in GET response for category, like this:

{
  "data": {
    "id": "1",
    "type": "categories",
    "attributes": {
      "name": "Book"
    },
    "relationships": {
      "posts": {
        "data": [{
          "id": "11",
          "type": "posts"
        }, {
          "id": "14",
          "type": "posts"
        }, {
          "id": "16",
          "type": "posts"
        }]
      }
    }
  },
  "included": [{
    "id": "11",
    "type": "posts",
    "attributes": {
      "style": false,
      "comments": true
    }
  }, {
    "id": "14",
    "type": "posts",
    "attributes": {
      "style": true,
      "comments": false
    }
  }, {
    "id": "16",
    "type": "posts",
    "attributes": {
      "style": true,
      "comments": false
    }
  }]
}

I think all this is not working the way I want maybe because of this (javascript - Ember 2, Strange behaviour with isPending, isSettled, isFulfilled when using included relationships in API response - Stack Overflow).

SHORTLY:

I don’t want many many many HTTP requests. Just one for category (model) and one (non blocking, with loading message) for posts (after the first one for model: category)…

If you include everything into the category request in order to reduce the calls, so there will not be loading status for your posts data. I think using related links from Lux on Stack (javascript - Ember 2, Strange behaviour with isPending, isSettled, isFulfilled when using included relationships in API response - Stack Overflow) is valid to you. However, if you really need to get all posts in specific category by using few requests as much as possible while preserving posts loading status at the same time, try the following way.

  • Get all category information once without posts sideloaded. (backend URL: /categories/1)
  • Get all posts in that category from your backend like /posts?category=1 or /categories/1/posts (You might need to modify your backend to support query parameters.)

Yes, but my problem is that also if I use related links to include posts like this:

{
  data: {
    type: 'categories',
    id: '1',
    attributes: {...},
    relationships: {
      posts: {
        links: {
          related: '/api/category/1/posts'
        }
      }
    }
  }
}

Ember always make hundreds HTTP (GET /post/12, GET /post/14, GET /post/16, GET /post/17 and so on…) requests instead of one (GET /category/1/posts).

And my Rails backend is giving it correctly if I navigate to “GET /category/1/posts”.

It’s acting like If links: {api/category/1/posts' is not there but there are relationships ids. It’s very strange.

Strange… I’ve simulated your structure and it gives me the correct queries.

Route:

model() {
  return this.store.findAll('board-category');
},

Template:

{{#each model as |category|}}
  <ul>
    <li>{{category.title}}
      <ul>
        {{#each category.topics as |topic|}}
          <li>{{topic.title}} (from category {{topic.category.title}})</li>
        {{/each}}
      </ul>
    </li>
  </ul>
{{/each}}

Category model:

topics: hasMany('board-topic'),

Topic model:

category: belongsTo('board-category'),

Data:

{
  "data": [
    {
      "type": "board-categories",
      "id": "1",
      "attributes": {
        "active": 1,
        "title": "Security",
      },
      "relationships": {
        "topics": {
          "links": {
            "related": "/board-categories/1/topics"
          }
        }
      }
    },
    ...
  ]
}

From Inspector:

Can you recheck your template and find out if there are anything using those queries in your controller/component?

Can you share your ember-twiddle?

Dear, sorry, now it works good. I’m using this template:

{{#each model.posts}}

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

(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”?

Hi, good to hear it works. :slight_smile:

About your “blinking”, it might be from your template bindings. IMHO, Glimmer would replace DOM wherever it changed. The more state changes and deeper hierarchy, the more “replace” actions.

Anyway, it seems a little more logical to me to do it this way.

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

Also with this it’s blinding, for example this code:

It’s your code exactly!

I see it’s blinking in twiddle. However, I couldn’t reproduce ANY “blink” issue in real application. For me, it seems to be twiddle problem not Ember. Can you recheck the code with real application?

That ember-twiddle is exactly my real application. The same problem. Just mirage vs real API endpoint.

I know, but running in twiddle gives me different result from running in my local with ember serve.

You’re referring to the red and green blink of the first attempt?

In twiddle yes, but local no, any.

It’s incredible. Because this user suggests me to add reload: true and it works!

Why this?

Maybe on your local machine you can’t see delay because it’s so fast?

I tested with vary delay from 0 to 4000 ms with everything random (from relationship, data, delay, including clicking on random category and delay for each click). I’m not sure that reload will be the solution or not since I rarely need it, but good to hear that it solves your problem. :wink:

No, this is not solving my problem. Because I don’t want this fix. It’s anti-pattern.