Ember 2,过滤关系模型(hasMany,belongsTo),并根据关系计算计算属性 [英] Ember 2, filter relationship models (hasMany, belongsTo) and calculate computed property based on relationships

查看:350
本文介绍了Ember 2,过滤关系模型(hasMany,belongsTo),并根据关系计算计算属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这些是我的文件:



模型



basket.js:

 导出默认DS.Model.extend({
name:DS.attr('string' ),
house:DS.belongsTo('house',{async:true}),
box:DS.hasMany('box',{async:true})
});

app / models / box.js:

  export default DS.Model.extend({
qty:DS.attr('number'),
basket:DS.belongsTo('basket'),
cartLines:DS.hasMany('cart-line',{async:true})
});

app / models / cart-line.js:

 导出默认DS.Model.extend({
qty:DS.attr('number'),
box:DS.belongsTo('box' ),
产品:DS.belongsTo('product')
});

app / models / product.js:

  export default DS.Model.extend({
name:DS.attr('string'),
price:DS.attr('number')
});

路线



app / routes / basket.js:

 导出默认值Ember.Route.extend({
model(params ){
return Ember.RSVP.hash({
basket:this.store.findRecord('basket',params.basket_id),
boxes:this.store.findAll('box' )
product:this.store.findAll('product')
});
},
setupController(controller,models){
controller.setProperties模型);
}
});

控制器



app / controllers / basket.js:

 导出默认值Ember.Controller.extend({
subTotal:Ember 。(计算机(.box)@ each.cartLines',function(){
return this.products.reduce((price,product)=> {
var total = price + product.get价格');
返回总额;
},0);
})
});

问题:



我是新手,所以我在学习和犯错误。对不起,



1)当我第一次进入路线时,哪个是最佳的Ember方式来过滤关系
例如,现在我加载我的应用程序中的每个框框:this.store.findAll('box')。我需要一种方法来加载我的webapp中的所有框,只是一个在篮子里。直接从后端需要查询过滤器?



更新问题
2)哪个是最好的Ember方法用于计算subTotal?现在,使用下面的代码,Ember给了我subTotal,但只是在 console.log(tot)和承诺之后!为什么这个?如何等待承诺?我不明白该怎么做:

  subTotal:Ember.computed('basket.boxes。@ each.cartLines' function(){
let count = 0;
console.log('subTotal called:',count);
//应该是0有
count = count + 1 ;

返回this.get('basket.boxes')然后(boxes => {
boxes.forEach(box => {
box.get cartLines')然后(cartLines => {
cartLines.reduce(function(tot,value){
console.log('tot:',tot + value.get('product.price' ));
返回tot + value.get('product.price');
},0);
});
});
}) ;
});

它给了我模板[object Object],因为我也在hbs中使用 {{log subTotal}} 并在控制台中给出了这一点:

  subTotal调用: 0 
ember.debug.js:10095 Class {__ember1476746185015:ember802,__ember_meta__:Meta}
subTotal调用:0
ember.debug.js:10095类{__ember1476746185015:ember934 ,__ember_meta__:Meta}
ember.debug.js:10095 Class {isFulfilled:true,__ember1476746185015:ember934,__ember_meta__:Meta}
subTotal调用:0
ember.debug.js: 10095 Class {__ember1476746185015:ember1011,__ember_meta__:Meta}
ember.debug.js:10095 Class {isFulfilled:true,__ember1476746185015:ember1011,__ember_meta__:Meta}
tot:3.5
tot:6
tot:13.5
tot:21
tot:24.5
tot:27
tot:3.5
tot:6
tot:13.5
tot:21
tot:24.5
tot:27
tot:3.5
tot:6
tot:13.5
tot :21
tot:24.5
tot:27

为什么它显示三次 subTotal调用:0 无论是零还是一千个产品。他总是打电话三次 subTotal调用:0 为什么



使用具有承诺的计算属性?



3)我是否使用该关系封装?



更新问题2



现在我使用这段代码,但没有成功:

 从ember导入Ember; 
从ember-data导入DS;

导出默认Ember.Controller.extend({

totalCount:Ember.computed('basket.boxes。@ each.cartLines',function(){
let total = 0;
const promise = this.get('basket.boxes')然后(boxes => {
boxes.map(box => {
// const trypromise = boxes.map(box => {
console.log('box:',box);
box.get('cartLines')然后(cartLines => {
console.log('cartLines:',cartLines);
const cartLinesPromise = cartLines.map(cartLine => {
console.log('cartLine:',cartLine);
/ / return cartLine.get('qty');
// return cartLine;
//});
return {
qty:cartLine.get('qty'),
price:cartLine.get('product.price')
};
// return cartLines.map(cartLine => {
// console。 log('cartLine:',cartLine);
// return cartLine.get('qty');
// // return {
// // qty:cartLine.get('qty'),
// // price:cartLine.get('product.price')
// //};
//});
})
//});
返回Ember.RSVP
.all(cartLinesPromise)
.then(cartLinesPromise => {
console.log('cartLinesPromise:',cartLinesPromise);
/ / cartLinesPromise.reduce((tot,price)=> {
// console.log('tot:',tot);
// console.log('price:',price);
// console.log('tot + price:',tot + price);
// return tot + price,0;
//});

return total = 10;
// return total;
})
});

});

// return total;
});

return DS.PromiseObject.create({promise});
})

})



在模板中,我使用:

  {{log'HBS totalCount:'totalCount}} 
{{log'HBS totalCount.content:'totalCount.content}}
总计:{{totalCount.content}}
/ pre>

承诺 null 内容



我错了什么?



任何不正确的返回



这段代码promising是否正确?

解决方案

没有什么不好的新技术,特别是当你的问题格式良好,思考的时候。



1)哪个是最好的Ember过滤关系的数据方式?



这是一个复杂的问题,有很多可能的结局。



最简单要做的就是要求这个模型。



在篮子上询问



给你的模型y ou可以做:

  model(params){
//我们将返回篮子,但准备好盒子
return this.get('store')。find('basket',params.basket_id).then(basket => {
return basket.get('boxes')。then(()=> basket);
});
}

但这没有什么限制和优势




  • 你需要发送ids与篮子

  • 你必须启用 coalesceFindRequests 使其理智化

  • 它将只加载不存储的框



编辑:
您需要发送带有ids的ID c $ c>这意味着您的有效载荷中的 basket 将必须为其框提供标识。如果休息api: {basket:{id:1,boxes:[1,2,3],...} 。然后,它将检查哪些ID尚未加载到商店中,并在此处询问api(假设已经加载了id 2的框): / boxes?ids [] = 1& ids [] = 3



问问自己



  params){
const store = this.get('store');
const basket = params.basket_id;

返回RSVP.hash({
model:store.find('basket',basket),
boxes:store.query('box',{basket}),
});
},




  • 另一方面,这种方法将发送请求篮子,只有当篮子不在商店(与之前相同)
    但总是查询框(如果你不喜欢它,你将不得不使用peekAll和过滤器来检查,如果你有全部或

  • 好的想法是,请求将并行不是连续的,因此可能会加快速度。

  • 篮子也不

  • 您可以通过更改查询 param

  • 进行服务器端过滤


编辑:
如果你不喜欢它,你必须使用peekAll并过滤检查你是否拥有所有的您可以使用

旁边他们



而不是向服务器发送两个请求,你可以使你的api这样做



仅加载篮子并放置从模板加载



您可以加载只有最低限度(如加载只有篮子),让ember继续并渲染页面。它会看到您正在访问
basket.boxes 属性并获取它们。这不会自己看起来很好,还需要一些额外的工作,如旋转器等。
但是这是一种如何加快启动和初始渲染时间的方式。



2)哪个是计算subTotal的最佳Ember方式? b
$ b

您想要计算三个层次深入异步关系的东西的总和,这并不容易。
首先我建议把totalPrice计算的属性放入篮子模型本身。计算属性
被懒惰地评估,所以没有性能下降,这是模型应该能够提供的。



这里是一个小代码片段:

  // basket.js 
const {RSVP,computed} = Ember;

价格:calculate('boxes。@ each.price',function(){
const promise = this.get('boxes')然后(boxes => {
//假设box.get('price')是这样的
//的计算属性,并返回承诺,因为框必须等待购物车行解决
const prices = boxes.map(box = > box.get('price'));

返回RSVP
.all(price)
.then(price => prices.reduce((carry,price )

返回PromiseObject.create({promise});
}),

你需要为每个级别写这样的东西,或放弃一些异步关系。
您的计算属性的问题是框。@ each.cartLines 不会听取可以改变整体价格的所有内容(例如产品本身价格的变化) 。所以它不会反映和更新所有可能的变化。



我将sagest放弃一些异步关系。例如, / baskets / 2 上的请求可能会卸载其所有框,cartLines甚至产品。
如果您的api不支持侧面加载,您可以通过加载路由中的所有内容来伪造它(您将不得不使用第二个示例 - 在<$ c的情况下,您不能在存储之前访问框$ c> async:false
)。
这将导致更简单的计算属性来计算总价格,并且在侧向加载的情况下也会降低服务器和客户端甜点的压力。

  // basket.js 
const {computed} = Ember;

box:DS.hasMany('box',{async:false}),

price:computed('boxes。@ each.price',function()
返回this.get('boxes')。reduce(box => box.get('price'));
}),
pre>

想法之后的更新和整体



我不认为在一个函数中做所有的总和是可行的,可行或理智。你最终会在回调地狱或其他一些地狱。此外,这不会是性能瓶颈。



我做了 jsfiddle它是上面的片段更丰富的版本。请注意,它将适当地等待和传播两个承诺的价格深度,并且应该更新当某事改变(也没有测试)。


These are my files:

Models

app/models/basket.js:

export default DS.Model.extend({
  name: DS.attr('string'),
  house: DS.belongsTo('house', { async: true }),
  boxes: DS.hasMany('box', { async: true })
});

app/models/box.js:

export default DS.Model.extend({
  qty: DS.attr('number'),
  basket: DS.belongsTo('basket'),
  cartLines: DS.hasMany('cart-line', { async: true })
});

app/models/cart-line.js:

export default DS.Model.extend({
  qty: DS.attr('number'),
  box: DS.belongsTo('box'),
  product: DS.belongsTo('product')
});

app/models/product.js:

export default DS.Model.extend({
  name: DS.attr('string'),
  price: DS.attr('number')
});

Routes

app/routes/basket.js:

export default Ember.Route.extend({
  model(params) {
    return Ember.RSVP.hash({
      basket: this.store.findRecord('basket', params.basket_id),
      boxes: this.store.findAll('box'),
      products: this.store.findAll('product')
    });
  },
  setupController(controller, models) {
    controller.setProperties(models);
    }
});

Controllers

app/controllers/basket.js:

export default Ember.Controller.extend({
  subTotal: Ember.computed('boxes.@each.cartLines', function () {
    return this.products.reduce((price, product) => {
      var total = price + product.get('price');
      return total;
    }, 0);
  })
});

Questions:

I'm newbie, so I'm studying and makings mistakes. Sorry.

1) Which is the best Ember way to filter relationships when I first enter in route? For example now I load every box in my app whith boxes: this.store.findAll('box'). I need a way to not load all the box in my webapp, just the one in basket. I need the "query with filter" directly from a backend?

UPDATED QUESTION 2) Which is the best Ember way for calculate subTotal? Now, with code below, Ember gives me the subTotal but just in console.log(tot) and after the promises! Why this? How can I wait the promises? I don't understand what to do:

subTotal: Ember.computed('basket.boxes.@each.cartLines', function () {
  let count = 0;
  console.log('subTotal called: ', count);
  // It should be 0 ever
  count = count + 1;

  return this.get('basket.boxes').then(boxes => {
    boxes.forEach(box => {
      box.get('cartLines').then(cartLines => {
        cartLines.reduce(function (tot, value) {
          console.log('tot:', tot + value.get('product.price'));
          return tot + value.get('product.price');
        }, 0);
      });
    });
  });
});

It gives me in template [object Object] because I'm also using in hbs {{log subTotal}} and in console it gives me this:

subTotal called:  0
ember.debug.js:10095 Class {__ember1476746185015: "ember802", __ember_meta__: Meta}
subTotal called:  0
ember.debug.js:10095 Class {__ember1476746185015: "ember934", __ember_meta__: Meta}
ember.debug.js:10095 Class {isFulfilled: true, __ember1476746185015: "ember934", __ember_meta__: Meta}
subTotal called:  0
ember.debug.js:10095 Class {__ember1476746185015: "ember1011", __ember_meta__: Meta}
ember.debug.js:10095 Class {isFulfilled: true, __ember1476746185015: "ember1011", __ember_meta__: Meta}
tot: 3.5
tot: 6
tot: 13.5
tot: 21
tot: 24.5
tot: 27
tot: 3.5
tot: 6
tot: 13.5
tot: 21
tot: 24.5
tot: 27
tot: 3.5
tot: 6
tot: 13.5
tot: 21
tot: 24.5
tot: 27

Why it shows three times subTotal called: 0, no matter if there are zero or one or a thousand products. He always calls three times subTotal called: 0, why?

Is it good to use computed properties with promises?

3) Am I right with that relationship encapsulation?

UPDATED QUESTION 2:

Now I'm using this code, but without success:

import Ember from 'ember';
import DS from 'ember-data';

export default Ember.Controller.extend({

  totalCount: Ember.computed('basket.boxes.@each.cartLines', function () {
    let total = 0;
    const promise = this.get('basket.boxes').then(boxes => {
      boxes.map(box => {
      // const trypromise = boxes.map(box => {
        console.log('box:', box);
        box.get('cartLines').then(cartLines => {
          console.log('cartLines:', cartLines);
          const cartLinesPromise = cartLines.map(cartLine => {
              console.log('cartLine:', cartLine);
              // return cartLine.get('qty');
              // return cartLine;
              // });
              return {
                qty: cartLine.get('qty'),
                price: cartLine.get('product.price')
              };
              //     return cartLines.map(cartLine => {
              //       console.log('cartLine:', cartLine);
              //       return cartLine.get('qty');
              //       //   return {
              //       //     qty: cartLine.get('qty'),
              //       //     price: cartLine.get('product.price')
              //       //   };
              //     });
            })
            // });
        return Ember.RSVP
          .all(cartLinesPromise)
          .then(cartLinesPromise => {
            console.log('cartLinesPromise:', cartLinesPromise);
            // cartLinesPromise.reduce((tot, price) => {
            //   console.log('tot:', tot);
            //   console.log('price:', price);
            //   console.log('tot+price:', tot + price);
            //   return tot + price, 0;
            // });

            return total = 10;
            // return total;
          })
        });

      });

      // return total;
    });

    return DS.PromiseObject.create({ promise });
  })

})

Comments are for many try.

In template I use:

{{log 'HBS totalCount:' totalCount}}
{{log 'HBS totalCount.content:' totalCount.content}}
Total: {{totalCount.content}}

But promise have null content.

Where I'm wrong?

Any incorrect return?

Is this code "promising" correct?

解决方案

There is nothing bad to being new to technology, especially when your question is well formatted and think through.

1) Which is the best Ember-Data way to filter relationships?

This is complex question with a lot of possible endings.

The easiest thing to do is just ask on that model.

Ask on basket

Given your model you can do:

model(params) {
  // we will return basket but make boxes ready
  return this.get('store').find('basket', params.basket_id).then(basket => {
    return basket.get('boxes').then(() => basket);
  });
}

But this has few limitations and advantages

Edit: you need to send ids with basket This means that basket in your payload will have to provide identification for it's boxes. In case of rest api: {basket: {id: 1, boxes: [1,2,3], ...}. It will then check which ids are not loaded into the store already and ask api here (assuming that box with id 2 is already loaded): /boxes?ids[]=1&ids[]=3.

Ask yourself

model(params) {
  const store = this.get('store');
  const basket = params.basket_id;

  return RSVP.hash({
    model: store.find('basket', basket),
    boxes: store.query('box', {basket}),
  });
},

  • On the other hand this approach will send request for basket only if basket is not in store already (same as before) but always query for boxes(if you don't like it you would have to use peekAll and filter to check if you have all of them or smt like that).
  • Good think is that the requests will be parallel not serial so it may speed things up.
  • Basket also doesn't have to send ids of its boxes.
  • You can do server side filtering by altering query param

Edit: if you don't like it you would have to use peekAll and filter to check if you have all of them You can actually check that with hasMany.

Sideload them

Instead of sending two requests to server you can make your api so that it will append boxes into the payload.

Load only basket and let rest to load from template

You can load only bare minimum (like load only basket), let ember continue and render the page. It will see that you are accessing basket.boxes property and fetch them. This wont look good on its own and will need some additional work like spinners and so on. But this is one way how to speed up boot and initial render time.

2) Which is the best Ember way for calculate subTotal

You want to calculate sum of something that is three levels deep into async relationships, that's not going to be easy. First of I would suggest putting totalPrice computed property into basket model itself. Computed properties are lazily evaluated so there is no performance degradation and this is something that model should be able to provide.

Here is little snippet:

// basket.js
const {RSVP, computed} = Ember;

price: computed('boxes.@each.price', function() {
  const promise = this.get('boxes').then(boxes => {
    // Assuming box.get('price') is computed property like this
    // and returns promise because box must wait for cart lines to resolve.
    const prices = boxes.map(box => box.get('price'));

    return RSVP
      .all(prices)
      .then(prices => prices.reduce((carry, price) => carry + price, 0));
  });

  return PromiseObject.create({promise});
}),

You would need to write something like this for each level or give up some of the async relations. The problem with your computed property is that boxes.@each.cartLines wont listen on everything that can change overall price (for example change of price of product itself). So it won't reflect and update on all possible changes.

I would sagest to give up some async relations. For example request on /baskets/2 could sideload all of its boxes, cartLines and maybe even products. If your api doesn't support sideloading, you can fake it by loading everything in route (you would have to use second example - you are not allowed to access boxes before they are in the store in case of async: false). That would lead to much simpler computed properties to calculate total price and in case of sideloading also reduce stress on server and clients confections.

// basket.js
const {computed} = Ember;

boxes: DS.hasMany('box', {async: false}),

price: computed('boxes.@each.price', function() {
  return this.get('boxes').reduce(box => box.get('price'));
}),

Update and overall after thoughts

I don't think that doing all sums in one function is viable, doable or sane. You will end up in callback hell or some other kind of hell. Moreover this is not going to be performance bottleneck.

I made jsfiddle it is basicaly more fleshed out version of snippet above. Note that it will properly wait and propagate price which is two promises deep and also should update when something changes (also I didn't test that).

这篇关于Ember 2,过滤关系模型(hasMany,belongsTo),并根据关系计算计算属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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