这些是我的文件:
模型
应用程序/模型/ 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
?
此代码“有前途”是否正确?
答案 0 :(得分:1)
对技术不熟悉并不是什么坏事,特别是当你的问题格式化并经过思考时。
这是一个复杂的问题,有很多可能的结局。
最简单的方法就是询问该型号。
鉴于您的模型,您可以这样做:
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);
});
}
但这没有什么限制和优点
修改强>
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}),
});
},
query
param 修改强>
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
属性并获取它们。这本身看起来不太好,需要一些额外的工作,如纺纱等。
但这是如何加快启动和初始渲染时间的一种方法。
您希望计算深度为异步关系的三个级别的总和,这并不容易。 首先,我建议将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
不会听取可能改变整体价格的所有内容(例如产品本身价格的变化)。因此,它不会反映并更新所有可能的更改。
我会妄图放弃一些异步关系。例如,/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这是基本上更加充实的上述代码段版本。请注意,它将正确地等待并传播价格,这是两个承诺的深度,并且当事情发生变化时也应该更新(我也没有测试过)。
答案 1 :(得分:0)
@ {Kingpin2k在How to return a promise composed of nested models in EmberJS with EmberData?中很好地解释了您的问题的解决方案。
你想要做的只是加载一个篮子及其相关的模型(盒子,猫线和产品)而不是加载所有盒子,cartLines和产品。另外,要计算subTotal,我们需要预先解决所有这些依赖性承诺。按照前面提到的帖子中给出的解决方案,您的解决方案将如下所示:
MODEL:app / models / cart-line.js
$(function() {
var limit = 10;
var current = 0;
$("button").on("click", function() {
if (++current < limit) {
$(".form-group").last().clone()
.find("label")
.text(function(_, text) {
return text.replace(/\d+/, current + 1)
})
.end()
.insertAfter(".form-group:last");
} else {
$(this).off("click").prop("disabled", "disabled");
}
});
})
ROUTE:app / routes / basket.js
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Add File</button>
<div class="form-group row">
<label for="file1_recipient" class="col-xs-2 col-form-label">Upload Photo (1)</label>
<div class="col-xs-10">
<input type="file" class="file">
</div>
</div>
CONTROLLER:app / controllers / basket.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
});
最后,对于你在这里遇到的问题:
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);
});
});
});
}
});
如果遵循上面给出的解决方案,你不会使用计算结构,但只想指出类似条件下的解决方案。
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;
})