问题底部有一个javascript
和coffescript
jsfiddle。
这两个小提琴都包含需要按特定顺序阅读的解释性评论,当您点击{{1>时打印出值console
}}或product
,另外我给你这个问题的基本解释。
submit div
,Purchase
和Product
Item
有多个Purchase
,一个 Products
有多个Product
Items
对象在Purchase
上设置了click event handler
,而$('submit')
会将onClick()
数据发布到我的后端api items
格式
data
My coffeescript jsfiddle is at the following link
点击下方打开 {
'purchase' => {
'items_attributes' => {
'0' => {
'purchase_id' => '1'
},
'1' => {
'purchase_id' => '2'
}
}
}
}
。
javascript fiddle
(function() {
var Item, Product, Purchase,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Purchase = (function() {
function Purchase() {
/* on $(document).ready a new Purchase is created */
this.submit = $('#submit');
/* for each div.product a new Product instance is created */
this.products = $.map($('.product'), function(product, i) {
return new Product(product);
});
/ @onSubmit() */
/* Comment 3)
My issue here is how to I access the this.items from the Purchase class and call serialize()?
onSubmit: function () {
@submit.click(function(){console.log(Product.serialize())};
} */
}
return Purchase;
})();
Product = (function() {
Product.items = [];
function Product(product) {
this.product = $(product);
this.id = this.product.data("id");
this.submit = $('#submit');
this.setEvent();
this.onSubmit();
}
Product.prototype.setEvent = function() {
return this.product.click((function(_this) {
return function() {
/* Comment 1)
Product.items is a class variable of Product, because I need to access it from the Purchase class and send post request. When the user clicks on the $('submit') button*/
Product.items.push(new Item(_this.id));
return console.log(Product.items);
};
})(this));
};
Product.prototype.onSubmit = function() {
return this.submit.click(function() {
/* Comment 2)
This works as you can see, but we have 4 products and this operation will
be performed 4 times. I want to achieve this in the Purchase object so it is perfomed only once, by creating a sumit event handler in Purchase */
return console.log(Product.serialize());
});
};
Product.serialize = function() {
var item;
return {
items_attributes: (function() {
var j, len, ref, results;
ref = Product.items;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
item = ref[j];
results.push(item.serialize());
}
return results;
})()
};
};
return Product;
})();
Item = (function() {
function Item(product_id) {
this.product_id = product_id;
this.serialize = bind(this.serialize, this);
}
Item.prototype.serialize = function() {
return {
product_id: this.product_id.toString()
};
};
return Item;
})();
$(document).ready(function() {
return new Purchase();
});
}).call(this);
.console {
background-color: grey;
color: white;
height: 500px;
} # I print to the console Product.items
h4 {
color: red;
width: 100%;
text-align: center;
}
在我编写opensource时,您可以查看我的commit history,specific commit并分叉项目
答案 0 :(得分:2)
我是Active Model Serializer宝石的粉丝,它现在是Rails的一部分。我会尝试通过向所有类添加序列化方法将此模式扩展到您的coffeescript中,并在将数据传递到服务器时调用它们。
我不确定您对projectname-projectversion.rpm
类的计划,所以这里有一个简单的模型,其中包含建议的Item
方法:
serialize
鉴于您的购买类将包含class Item
constructor: (@purchase, @product, @quantity) ->
serialize: =>
purchase_id: @purchase.id.toString()
product_id: @product.id.toString()
quantity: parseInt(@quantity)
数组,那么@items
的{{1}}方法将如下所示:
Purchase
然后你的ajax帖子会使用serialize
方法:
serialize: =>
items_attributes: (item.serialize() for item in @items)
然后你应该得到一个
的JSON帖子体serialize
你可以通过强大的参数在你的rails控制器中使用:
$.ajax
url: "/items"
method: "POST"
dataType: "json"
data:
purchase: @serialize()
error: (jqXHR, textStatus, errorThrown) ->
success: (data, textStatus, jqXHR) ->
答案 1 :(得分:2)
初始化时,您可以简单地将事件绑定到Purchase
对象中。
this.submit.click(function() {
return console.log(Product.serialize());
});
工作代码段:我已对onSubmit
Product
进行了评论。
(function() {
var Item, Product, Purchase,
bind = function(fn, me) {
return function() {
return fn.apply(me, arguments);
};
};
Purchase = (function() {
function Purchase() {
/* on $(document).ready a new Purchase is created */
this.submit = $('#submit');
/* for each div.product a new Product instance is created */
this.products = $.map($('.product'), function(product, i) {
return new Product(product);
});
/ @onSubmit() */
/* Comment 3)
My issue here is how to I access the this.items from the Purchase class and call serialize()?
onSubmit: function () {
@submit.click(function(){console.log(Product.serialize())};
} */
this.submit.click(function() {
return console.log(Product.serialize());
});
}
return Purchase;
})();
Product = (function() {
Product.items = [];
function Product(product) {
this.product = $(product);
this.id = this.product.data("id");
this.submit = $('#submit');
this.setEvent();
// this.onSubmit();
}
Product.prototype.setEvent = function() {
return this.product.click((function(_this) {
return function() {
/* Comment 1)
Product.items is a class variable of Product, because I need to access it from the Purchase class and send post request. When the user clicks on the $('submit') button*/
Product.items.push(new Item(_this.id));
return console.log(Product.items);
};
})(this));
};
// Product.prototype.onSubmit = function() {
// return this.submit.click(function() {
// /* Comment 2)
// This works as you can see, but we have 4 products and this operation will
// be performed 4 times. I want to achieve this in the Purchase object so it is perfomed only once, by creating a sumit event handler in Purchase */
// return console.log(Product.serialize());
// });
// };
Product.serialize = function() {
var item;
return {
items_attributes: (function() {
var j, len, ref, results;
ref = Product.items;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
item = ref[j];
results.push(item.serialize());
}
return results;
})()
};
};
return Product;
})();
Item = (function() {
function Item(product_id) {
this.product_id = product_id;
this.serialize = bind(this.serialize, this);
}
Item.prototype.serialize = function() {
return {
product_id: this.product_id.toString()
};
};
return Item;
})();
$(document).ready(function() {
return new Purchase();
});
}).call(this);
.console {
background-color: grey;
color: white;
height: 500px;
}
h4 {
color: red;
width: 100%;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul>
<li class="product" data-id="1">Product 1</li>
<li class="product" data-id="2">Product 2</li>
<li class="product" data-id="3">Product 2</li>
<li class="product" data-id="4">Product 3</li>
<li class="product" data-id="5">Product 4</li>
<button type="button" id="submit">Create Purchase</button>
</ul>
<h4>check logs by opening the console</h4>
答案 2 :(得分:1)
我已根据代码评论中的问题更新了您的咖啡因小提琴。
这是我的updated version。
我已经改变了你的类结构,因此不需要任何静态变量,在这种情况下看起来像是一个糟糕的设计。
您已将模型结构创建为:
但您的发布数据格式要求显示:
为了解决这种不一致问题,我将产品中的序列化数据展平,以便items_attributes
是序列化项目对象的数组:
class Purchase
...
serialize: =>
items = (product.serialize() for product in @products)
# flatten array of array of items:
items_attributes: [].concat.apply([], items)
这个神秘的线条[].concat.apply([], items)
是展平嵌套数组的一层深度的简写(取自answer)。
现在,每个产品实例都会自行保存一系列项目,而不是静态地保存在类中。
class Product
constructor: (product) ->
@product = $(product)
@id = @product.data("id")
@submit = $('#submit')
@items = []
@registerEvents()
addItem: =>
console.log "added item #{@id}"
@items.push new Item(@id)
registerEvents: ->
@product.click @addItem
serialize: =>
(item.serialize() for item in @items)
我认为更好地重新设计此类结构将删除Product
或Item
类,因为只有产品ID,据我所知,项目就像计算购买产品的数量单位的计数器。您可以在产品上保留一个整数值,而不是为此设置类:
作为fiddle
class Purchase
constructor: () ->
# on $(document).ready a new Purchase is created
@submit = $('#submit')
# for each div.product a new Product instance is created
@products = $.map $('.product'), (product, i) ->
new Product(product)
@registerEvents()
onSubmit: =>
console.log "send to server..."
console.log JSON.stringify(@serialize(), null, 2)
registerEvents: ->
@submit.click @onSubmit
serialize: =>
items_attributes: (product.serialize() for product in @products when product.amount isnt 0)
class Product
constructor: (product) ->
@product = $(product)
@id = @product.data("id")
@submit = $('#submit')
@amount = 0
@registerEvents()
addItem: =>
console.log "added item #{@id}"
@amount++
registerEvents: ->
@product.click @addItem
serialize: =>
product_id: @id
amount: @amount
输出现在看起来不同但是IMO更清洁:
新强>
{
"items_attributes": [
{
"product_id": 1,
"amount": 1
},
{
"product_id": 2,
"amount": 3
}
]
}
<强>旧强>
{
"items_attributes": [
{
"product_id": "1"
},
{
"product_id": "2"
},
{
"product_id": "2"
},
{
"product_id": "2"
}
]
}
但是这可能不适合您当前的后端实现,具体取决于当前处理重复项的方式,因此如果无法更改任何遗留约束,请忽略此最后一部分。
最后,我想补充一点,这个&#34;面向对象&#34;将事件侦听器和逻辑附加到DOM的方法比在加载时执行的典型jquery函数更加结构化。但我过去曾经使用它,并且保持DOM结构和代码更新是一件痛苦的事情,并且经常会因为代码更改而导致错误,而另一方则没有镜像。
作为替代方案,我强烈建议调查reactjs或类似的DOM抽象类型库。这些允许您将逻辑强烈地耦合到它们所依赖的视图元素。
虽然通常与JSX一起使用,但它与Coffeescript配合得很好,但是这方面的资源非常少。 Arkency写了一篇关于react + coffeescript的好博客,我也写了一篇短文comparing coffeescript to jsx。