簡體   English   中英

Ember 2,過濾關系模型(hasMany,belongsTo)並根據關系計算計算屬性

[英]Ember 2, filter relationship models (hasMany, belongsTo) and calculate computed property based on relationships

這些是我的文件:

楷模

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

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

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

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

應用程序/模型/車,line.js:

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

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

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

路線

應用程序/路由/ 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);
    }
});

控制器

應用程序/控制器/ 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);
  })
});

問題:

我是新手,所以我正在學習和制造錯誤。 抱歉。

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

它給了我模板[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

為什么它顯示三次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 });
  })

})

評論是很多嘗試。

在模板中我使用:

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

promise內容是null

哪里我錯了?

任何錯誤的return

這段代碼“有前途”是否正確?

對技術不熟悉並不是什么壞事,特別是當您的問題格式正確並經過深思熟慮時。

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

但這幾乎沒有局限性和優勢

  • 你需要發送帶籃子的ids
  • 你必須啟用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}),
  });
},
  • 另一方面,這種方法只有在籃子已經存放時才會發送籃子請求(與以前相同)但總是查詢盒子(如果你不喜歡它,你必須使用peekAll並過濾來檢查你是否有所有這些或像這樣的smt)。
  • 好的想法是請求將是並行的而不是串行的,因此可能會加快速度。
  • 籃子也不必發送其盒子的ID。
  • 您可以通過更改query參數來執行服務器端篩選

編輯: if you don't like it you would have to use peekAll and filter to check if you have all of them你可以用hasMany實際檢查。

側載他們

而不是向服務器發送兩個請求,您可以創建您的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});
}),

您需要為每個級別編寫類似的內容或放棄一些異步關系。 你的計算屬性的問題是boxes.@each.cartLines不會聽取所有可以改變整體價格的東西(例如產品本身價格的變化)。 因此,它不會反映和更新所有可能的更改。

我會sagest放棄一些異步關系。 例如, /baskets/2上的請求可以側載所有的盒子,cartLines甚至產品。 如果您的api不支持sideloading,您可以通過加載路由中的所有內容來偽造它(您必須使用第二個示例 - 在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'));
}),

思考后更新和整體

我不認為在一個功能中做所有的總和是可行的,可行的或理智的。 你將最終陷入回調地獄或其他一些地獄。 此外,這不會成為性能瓶頸。

我做了jsfiddle它是基本上更加充實的上面的片段版本。 請注意,它將正確地等待並傳播價格,這是兩個承諾的深度,並且當事情發生變化時也應該更新(我也沒有測試過)。

如何使用EmberData返回EmberJS中嵌套模型組成的承諾,可以很好地解釋您的問題的解決方案 來自@ Kingpin2k。

你想要做的只是裝一個籃子及其相關的模型(盒子,貓線和產品),而不是裝載所有的盒子,cartLines和產品。 另外,要計算subTotal,我們需要預先解決所有這些依賴性承諾。 按照前面提到的帖子中給出的解決方案,您的解決方案將如下所示:

MODEL:app / models / cart-line.js

export default DS.Model.extend({
  qty: DS.attr('number'),
  box: DS.belongsTo('box'),
  product: DS.belongsTo('product', { async: true })//assuming you are not side-loading these
});

ROUTE:app / routes / basket.js

export default Ember.Route.extend({
    model(params) {
        return this.store.findRecord('basket', params.basket_id).then((basket)=> {
            return basket.get('boxes').then((boxes)=> {
                let cartLinesPromises = boxes.map(function (box) {
                    return box.get('cartLines');
                });
                return Ember.RSVP.allSettled(cartLinesPromises).then((array)=> {
                    let productPromises = array.map(function (item) {
                        return (item.value).get('product');
                    });
                    return Ember.RSVP.allSettled(productPromises);
                });
            });
        });
    }
});

控制器:app / controllers / basket.js

subTotal: computed('model.boxes.@each.cartLines', function () {
    //you dont need to use DS.PromiseArray because the promises all have been already resolved in the route's model hook
    let total = 0;
    this.get('model.boxes').forEach((box)=> {
        box.get('cartLines').forEach((cartLine)=> {
            total += cartLine.get('product.price');
        });
    });
    return total;
})

最后,對於你在這里遇到的問題:

subTotal: computed('boxes.@each.cartLines', function() {
  return DS.PromiseArray.create({
    //"this" here is DS.PromiseArray object and not your controller instance
    promise: this.get('boxes').then(boxes => {
      return boxes.filter(i => i.get('cart-line'));
    })
  });
})

如果遵循上面給出的解決方案,你不會使用計算結構,但只是想指出類似條件下的解決方案。

subTotal: computed('boxes.@each.cartLines', function() {
  let controllerInstance = this;
  return DS.PromiseArray.create({
    promise: controllerInstance.get('boxes').then(boxes => {
      return boxes.filter(i => i.get('cart-line'));
    })
  });
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM