Ember 2,使用isPending,isSettled的异常行为,在API响应中使用包含的关系时已经完成 [英] Ember 2, Strange behaviour with isPending, isSettled, isFulfilled when using included relationships in API response

查看:59
本文介绍了Ember 2,使用isPending,isSettled的异常行为,在API响应中使用包含的关系时已经完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要了解一切为什么财产如



isSettled
isPending
已完成



如果我包含或者不是我的API中的数据。



我在这里询问: https://discuss.emberjs.com/t / ember-2-show-a-single-loading-message-when-the-ids-which-included-in-the-original-response / 12654 ,这导致我这个奇怪的行为:



如果我包含在我的API中响应数据(例如: model.posts ),这些属性将立即设置为true code> .isPending 为false),如果Chrome仍在加载真实数据(也是第一次!)。



这是一个问题,因为我不知道帖子[] 是否为空,我不知道我可以间谍,因为这样的事情不起作用:

  {{#each model.posts}} 
我的帖子
{{else}}
{{#if model.posts.isPending}}
< div>正在加载...< / div>
{{else}}
< div>没有显示。< / div>
{{/ if}}
{{/ each}}

总是无事可做。直到Chrome加载。因为 .isPending 立即 false



另外如果我使用长度属性:



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



因为开始的帖子[] 数组和空的一个总是长度为== 0。



如果我加载不同的帖子,异步,而不是侧面加载(但是有数百个HTTP请求,我不想要)它的工作。 Ember认识一个 isPending ...



为什么是这个奇怪的行为?



更新



我的API响应 / category / 1

$ b

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

因为我正在使用包含在我的Rails控制器。



其他策略?




  • 嵌入:ids?


  • JSON API链接? >




如何做?



我只有一点点BABY PROBLEM:



我不希望有很多很多HTTP请求。只有一个类别(模型)和一个(非阻塞,加载消息)为第一个模型(类别)后的帖子...

解决方案

好的,我想先要告诉你有关加载数据的不同方法。在你的例子中,你有两个模型。我知道一个叫 post ,现在可以调用另一个 my-model



现在你们之间有很多关系。可能这样在 my-model 中:

 帖子:hasMany('帖子'),

现在显然你想显示所有 post s给定的 my-model 。为此,您可能有一条路线,如 / my-model-posts /:myModel_id 。一个非常有趣的问题是你如何链接到这条路线。这是因为一个链接你已经需要 my-model 实例,现在问题是你如何加载...


$ b $我稍后会谈一谈,现在让我们假设你的用户直接在这个给定的url上点击你的应用程序,因为这是你经常需要处理的一个用例。



由于命名约定 model_id ,您不需要自己编写路由。但是请记住,默认实现将等同于此显式路由定义:

  model(params){
return this.store.findRecord('my-model',params.myModel_id);
}

现在后端基本上有3种方式可以回复:



sideload一切

  {
data:{
type:'my-model',
id:'foo',
属性:{...},
关系:{
posts:{
data:[{
type:'post',
id:'1'
},{
type:'post',
id:'2 '
}]
}
}
},
包含:[{
type:'post',
id:'1' ,
属性:{...}
},{
类型:'post',
id:'2',
属性:{...}
}]
}

侧边的ids

  {
data:{
type:'my-model',
id: 'foo',
属性:{...},
关系:{
posts:{
data:[{
type:'p ost',
id:'1'
},{
type:'post',
id:'2'
}]
}
}
}
}

使用相关链接

  {
data:{
type:'my-model',
id:'foo',
属性:{...},
关系:{
posts:{
links:{
related:'/ api / model / foo / posts'
}
}
}
}
}

现在您需要了解 findRecord 返回的承诺将等待服务器的第一个响应,然后解析。您的路由器将在进入路由之前等待此响应。这是非常重要的理解:您的路线将不会输入,直到这个承诺解决。要在此阶段指示加载状态,您可以使用一个装载子状态



所以第一个例子,如果你加载所有这一切就够了。请记住,当返回响应时,不再需要加载数据。此外,直到此响应被加载,您正在加载子状态。



最后一个例子也不是很难。一个简单的方法是显示两个加载旋转器。您需要这个,因为您提出两个请求,并处于两种加载状态:


  1. 用户点击路线

  2. 您将 findRecord 请求

  3. 显示装载子状态,直到 findRecord 请求已经完成

  4. 返回响应,模板将被渲染

  5. 您访问 model.posts ,这将触发相关链接加载。数据正在加载 model.posts.isPending 将为 true 。您可以使用简单的 {{#如果model.posts.isPending}}加载... {{/ if}} 表示加载状态

  6. 返回响应, model.posts.isPending 现在是 false 。所有数据现在加载。

但是,如果需要,您可以将其简化为单个加载状态。您可以在 post 钩子:

  afterModel( model){
return model.get('posts');
}

这将执行承诺帖子在输入路由之前加载,让您进入加载子代。



另一件事是直接加载帖子,如果你不在乎 my-model 实例:

  model(params){
return this.store.findRecord('my-model',params.myModel_id)
.then(m => m.get('posts'));
}

现在模型将作为你的帖子数组,你将保留在加载子状态,直到帖子被加载。



最后但并非最不重要的是谈谈第二个例子:你只加载 ID 秒。这可能是最棘手的一个。如果您只负责加载 id s,则您没有代表某个某个仍然加载的属性。这是因为有许多HTTP请求,以及许多承诺,表明某些东西仍在加载。可能唯一的用例是当您真的想要尽快向用户显示数据时,所以我建议每个帖子使用一个加载微调器:

  {{#each model.posts as | post |}} 
{{#if post.isLoading}}
loading ...
{{else}}
...数据...
{{/ if}}
{{else}}
没有帖子
{{/ each}}

请注意,如果您没有发布任何信息,则必须先完成一个HTTP请求! p>

如果您想要一个加载微调框,您可以在 my-model 上创建一个计算属性:

  hasLoadingPosts:Ember.computed('posts。@ each.isLoading',{
get(){
return this。 get('posts')。any(post => post.get('isLoading'));
}
})

您也可以将其保存在装载子状态中,但这有点麻烦:

  afterModel(model){
return mo del.get('posts')
.then(posts => Ember.RSVP.all(posts.toArray()));
}

您可以在模型中做类似的事情 hook:

  model(params){
return this.store.findRecord('my-模型',params.myModel_id)
.then(m => m.get('posts'))
.then(posts => Ember.RSVP.all(posts.toArray()) );
}

还要注意,最后两个promise处理的JS代码段在所有场景中都可以工作。如果数据已经被加载,但是等待相关的链接以及侧面加载的ids,那么它们不会受到损害。



现在很重要的一点是, >通常不直接链接到这样的页面。可能你有一个列表 my-model s或某事,然后一个链接到来显示帖子。 这里你不会碰到模型钩子!另外你不需要这个第一个请求: my-model的数据已经在商店!现在的问题是当您首先加载时返回的问题。你有没有装载所有东西,只有 id 或使用相关的链接?后果与上述情况相似!



如果您修改了您的模型钩子,您可能应该更改您的 link-to 以及 {{#link-to'my-model-posts'model}} to {{#link-to'my-model-posts'model.posts}} 。这是花哨的,因为如果你使用了一个相关的链接,路线将再次等待这个承诺解决。






一般我建议不要只加载ids。这也可能仅在您不使用关系数据库作为后端时才有用。



我建议在极少数情况下使用相关链接并附加所有内容。例如,如果您直接加载了一个 my-model ,我将卸载所有内容,而列表只返回相关链接。



现在只是程序,因为只有相关的链接和事情通常只能工作。如果你有时需要一个加载指示器,这很好。



另外如果你只是显示这个数组的帖子,我会改变路由的语义,直接在数组中返回在模型中钩子。当然,如上所述,将链接更改为



但是,如果要显示某些数据 my-model 像标题一样,尽可能向他们显示。这里您需要 isPending ,而您显示标题,但尚未发布。



这两个解决方案但是,如上所述,当您卸载 id 时,将会中断。


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: https://discuss.emberjs.com/t/ember-2-show-a-single-loading-message-when-the-ids-where-included-in-the-original-response/12654 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?

UPDATE

My API response for /category/1:

{
  "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
    }
  }]
}

because I'm using include in my Rails controller.

Other strategies?

  • embedded :ids?

  • JSON API links?

How to do?

MY ONLY POOR LITTLE BABY PROBLEM:

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)...

解决方案

Okay, I think I need to tell you first about the different ways you can load the data. In your example you have two models. I know one is called post, and for now lets call the other one my-model.

Now you have a to-many relationship between them. Probably like this in my-model:

posts: hasMany('post'),

Now obviously you want to show all posts for a given my-model. For this you probably have a route like /my-model-posts/:myModel_id. A very interesting question is how you link to this route. This because for a link you already need the my-model instance, and now the question is how you loaded that...

I will talk about this later, for now lets assume your user directly hits your app at this given url, because this is a use-case you always have to handle.

Because the naming convention model_id you don't have to write your route yourself. However lets remember that the default implementation would be equivalent to this explicit route definition:

model(params) {
  return this.store.findRecord('my-model', params.myModel_id);
}

Now the backend basically has 3 ways to respond:

sideload everything

{
  data: {
    type: 'my-model',
    id: 'foo',
    attributes: {...},
    relationships: {
      posts: {
        data: [{
          type: 'post',
          id: '1'
        },{
          type: 'post',
          id: '2'
        }]
      }
    }
  },
  included: [{
    type: 'post',
    id: '1',
    attributes: {...}
  }, {
    type: 'post',
    id: '2',
    attributes: {...}
  }]
}

sideload the ids

{
  data: {
    type: 'my-model',
    id: 'foo',
    attributes: {...},
    relationships: {
      posts: {
        data: [{
          type: 'post',
          id: '1'
        },{
          type: 'post',
          id: '2'
        }]
      }
    }
  }
}

use a related link

{
  data: {
    type: 'my-model',
    id: 'foo',
    attributes: {...},
    relationships: {
      posts: {
        links: {
          related: '/api/model/foo/posts'
        }
      }
    }
  }
}

Now you need to understand that the promise returned by findRecord will wait for this first response of your server and then resolve. Your router will wait for this response before entering the route. This is very important to understand: Your route will not be entered until this promise resolves. To indicate the loading state during this phase you can use a loading substate..

So for the first example, if you sideload everything this is enough. Remember that when the response is returned there is no more data to load. Also until this response is loaded you are in the loading substate.

The last example is also not very hard. An easy way is to show two loading spinners. You need this, because you make two requests and are in two loading states:

  1. user hits the route
  2. you make the findRecord request
  3. the loading substate is shown until the findRecord request has finished
  4. the response is returned and the template will be rendered
  5. you access model.posts and this will trigger the related link to load. While the data is loading model.posts.isPending will be true. You can use a simple {{#if model.posts.isPending}}loading...{{/if}} to indicate the loading state
  6. the response is returned and model.posts.isPending is now false. All data is now loaded.

However you can hack around to reduce this to one single loading state if you want. You could just load the posts in your afterModel hook:

afterModel(model) {
  return model.get('posts');
}

This will enforce the promise posts to be loaded before the route is entered, keeping you in the loading substate.

Another thing is to directly load the posts, if you don't care about the my-model instance:

model(params) {
  return this.store.findRecord('my-model', params.myModel_id)
    .then(m => m.get('posts'));
}

Now model will be your posts array and you will stay in the loading substate until the posts are loaded.

Last but not least lets talk about the second example: You sideload only ids. This is probably the most tricky one. If you only side load the ids, you have no single property represententing that something is still loaded. This is because there are many HTTP requests, and so many promises indicating that something is still loading. Probably the only use-case for this is when you actually want to show the data to the user ASAP, so I would recommend a loading-spinner per post:

{{#each model.posts as |post|}}
  {{#if post.isLoading}}
    loading...
  {{else}}
    ...the data...
  {{/if}}
{{else}}
  no posts
{{/each}}

Notice that if you have no posts this is something you know before you have to make a single HTTP request!

If you want a single loading spinner you could create a computed property on the my-model:

hasLoadingPosts: Ember.computed('posts.@each.isLoading', {
  get() {
    return this.get('posts').any(post => post.get('isLoading'));
  }
})

You can also keep this in the loading substate however this is a bit more tricky:

afterModel(model) {
  return model.get('posts')
    .then(posts => Ember.RSVP.all(posts.toArray()));
}

You can do something similar in the model hook:

model(params) {
  return this.store.findRecord('my-model', params.myModel_id)
    .then(m => m.get('posts'))
    .then(posts => Ember.RSVP.all(posts.toArray()));
}

Notice also that these last two promise-handling JS-snippets work in all scenarios. They don't harm if the data is already loaded, but wait for a related link as well as for sideloaded ids.

Now something very important to notice is that usually don't direct-link to such a page. Probably you have somewhere a list of my-models or something, and then a link-to to show the posts. Here you wont hit the model hook! Also you don't need this first request: The data of my-model are already in the store! And now the question is what you returned when you loaded it in the first place. Have you sideloaded everything, only the ids or used a related link? The consequences are similar to the scenarios above!

If you modified your model hook you probably should change your link-to as well, from {{#link-to 'my-model-posts' model}} to {{#link-to 'my-model-posts' model.posts}}. This is fancy, because if you used a related link the route will again wait for this promise to resolve.


Generally my recommendation is to never sideload ids only. This is also probably only useful if you don't use a relational database for the backend.

I recommend to use related links and sideload everything in rare cases. For example if you directly load a my-model I would sideload everything, and for the list only return related links.

Now just program as there were only related links and things will generally just work. If you sometimes need a loading indicator less this is fine.

Also if you just show this array of posts I would change the semantic of the route and directly return this in array in the model hook. Of course then change the link-tos as explained above.

However if you want to show some data of my-model like a title it is probably desirable to show them ASAP. Here you need isPending while you show the title but don't have the posts yet.

Both of these solutions however will break when you sideload the ids only, as explained above.

这篇关于Ember 2,使用isPending,isSettled的异常行为,在API响应中使用包含的关系时已经完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆