Ember 2,过滤关系模型(hasMany,belongsTo)并根据关系计算计算属性

7 javascript relationship ember.js ember-data ember-controllers

这些是我的文件:

楷模

应用程序/模型/ basket.js:

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

应用程序/模型/ box.js:

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

应用程序/模型/车,line.js:

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

应用程序/模型/ product.js:

export default DS.Model.extend({
  name: DS.attr('string'),
  price: DS.attr('number')
});
Run Code Online (Sandbox Code Playgroud)

路线

应用程序/路由/ 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);
    }
});
Run Code Online (Sandbox Code Playgroud)

控制器

应用程序/控制器/ 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);
  })
});
Run Code Online (Sandbox Code Playgroud)

问题:

我是新手,所以我正在学习和制造错误.抱歉.

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

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

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);
      });
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

为什么它显示三次subTotal called: 0,无论是零还是一千或一千种产品.他总是打三次电话subTotal called: 0,为什么

将计算属性与promises一起使用是否合适?

3)我对这种关系封装是否正确?

更新的问题2:

现在我正在使用此代码,但没有成功:

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 });
  })

})
Run Code Online (Sandbox Code Playgroud)

评论是很多尝试.

在模板中我使用:

{{log 'HBS totalCount:' totalCount}}
{{log 'HBS totalCount.content:' totalCount.content}}
Total: {{totalCount.content}}
Run Code Online (Sandbox Code Playgroud)

promise要有null内容.

哪里我错了?

return什么不对吗

这段代码"有前途"是否正确?

Keo*_*Keo 1

刚接触技术并没有什么坏处,尤其是当你的问题格式良好且经过深思熟虑时。

1) 哪种 Ember-Data 过滤关系的最佳方式是?

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

最简单的事情就是询问该模型。

询问购物篮

鉴于您的模型,您可以执行以下操作:

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);
  });
}
Run Code Online (Sandbox Code Playgroud)

但这有一些限制和优点

  • 您需要发送带篮子的 ID
  • 你必须启用coalesceFindRequests才能使其理智
  • 它只会加载商店中没有的盒子

编辑: you need to send ids with basket这意味着basket在您的有效负载中必须提供其盒子的标识。如果是休息 API:{basket: {id: 1, boxes: [1,2,3], ...}. 然后它会检查哪些 id 尚未加载到商店中,并在此处询问 api(假设 id 2 的框已加载):/boxes?ids[]=1&ids[]=3

问你自己

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}),
  });
},
Run Code Online (Sandbox Code Playgroud)
  • 另一方面,仅当购物篮尚未入库(与以前相同)时,此方法才会发送购物篮请求,但始终查询盒子(如果您不喜欢它,则必须使用 peekAll 和过滤器来检查您是否有所有这些或类似的 smt)。
  • 好的想法是请求将是并行的而不是串行的,因此它可能会加快速度。
  • Basket 也不必发送其盒子的 ID。
  • 您可以通过更改query参数来进行服务器端过滤

编辑:您实际上可以使用 hasManyif you don't like it you would have to use peekAll and filter to check if you have all of them进行检查。

旁加载它们

您可以创建 api,以便将框附加到有效负载中,而不是向服务器发送两个请求。

仅加载篮子并让其休息以从模板加载

您可以仅加载最少的内容(例如仅加载购物篮),让 ember 继续并渲染页面。它会看到您正在访问 basket.boxes属性并获取它们。这本身看起来不太好,并且需要一些额外的工作,例如旋转器等。但这是加快启动和初始渲染时间的一种方法。

2) 计算 subTotal 的最佳 Ember 方法是什么

您想要计算深入到异步关系的三个级别的总和,这并不容易。首先,我建议将 TotalPrice 计算属性放入购物篮模型本身。计算属性是延迟评估的,因此不会出现性能下降,这是模型应该能够提供的。

这是一个小片段:

// 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});
}),
Run Code Online (Sandbox Code Playgroud)

您需要为每个级别编写类似的内容或放弃一些异步关系。您的计算属性的问题在于,它boxes.@each.cartLines不会监听所有可能改变总体价格的内容(例如产品本身价格的变化)。因此它不会反映和更新所有可能的更改。

我会明智地放弃一些异步关系。例如,请求/baskets/2可以侧载其所有盒子、cartLines 甚至产品。如果你的 api 不支持侧面加载,你可以通过加载路由中的所有内容来伪造它(你必须使用第二个示例 - 在盒子进入商店之前,你不能访问盒子)async: false。这将导致计算总价格的计算属性变得更加简单,并且在侧载的情况下还可以减轻服务器和客户端的压力。

// 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'));
}),
Run Code Online (Sandbox Code Playgroud)

更新和整体思考后

我认为在一个函数中完成所有求和是不可行的、可行的或明智的。你最终会陷入回调地狱或其他类型的地狱。而且这不会成为性能瓶颈。

我制作了jsfiddle,它基本上是上面代码片段的更充实的版本。请注意,它会正确等待并传播价格,这是两个深度承诺,并且在发生变化时也应该更新(我也没有测试这一点)。