javascript 对象 - 检索子 class 变量
javascript objects - retrieve child class variable
题底有javascript
和coffescript
jsfiddle。
两者 fiddle 都包含 需要按特定顺序阅读的解释性注释 ,它 将值打印到 console
当你点击 product
或 submit div
时,另外我给你这个问题的基本解释。
- 我有 3 个 Javascript 类
Purchase
、Product
和 Item
- 一个
Purchase
有很多Products
,一个Product
有很多Items
Purchase
对象在 $('submit')
上设置了一个 click event handler
,onClick()
将 post items
数据发送到我的后端 api
这是从 my backend api
接受的 data
格式
{
'purchase' => {
'items_attributes' => {
'0' => {
'purchase_id' => '1'
},
'1' => {
'purchase_id' => '2'
}
}
}
}
My coffeescript jsfiddle is at the following link
点击下面打开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;
}
<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>
<div id="submit">Create Purchase</div>
</ul>
<h4>check logs by opening the console</h4>
在我编写开源代码时,您可以查看我的 commit history, the specific commit 并 fork 项目
我是 Active Model Serializer gem 的粉丝,它现在是 Rails 的一部分。我会尝试通过向所有 classes 添加一个序列化方法,将这种模式扩展到您的 coffeescript 中,并在您将数据传递到服务器时调用这些方法。
我不确定你对 Item
class 的计划,所以这里有一个使用建议的 serialize
方法的简单模型:
class Item
constructor: (@purchase, @product, @quantity) ->
serialize: =>
purchase_id: @purchase.id.toString()
product_id: @product.id.toString()
quantity: parseInt(@quantity)
鉴于您的购买 class 将有一个 @items
的数组,那么 Purchase
的 serialize
方法将如下所示:
serialize: =>
items_attributes: (item.serialize() for item in @items)
然后你的 ajax post 将使用 serialize
方法:
$.ajax
url: "/items"
method: "POST"
dataType: "json"
data:
purchase: @serialize()
error: (jqXHR, textStatus, errorThrown) ->
success: (data, textStatus, jqXHR) ->
那么你应该得到 JSON post 的
正文
'purchase' => {
'items_attributes' => [
{
'purchase_id' => '1'
},
{
'purchase_id' => '2'
}
]
}
您可以通过强大的参数在您的 rails 控制器中使用它:
params.require(:purchase).permit(item_attributes: [:purchase_id])
初始化时,您可以简单地将事件绑定到 Purchase
对象中。
this.submit.click(function() {
return console.log(Product.serialize());
});
工作代码段: 我已经注释掉 Product
的 onSubmit
。
(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>
我已经更新了您的 coffeescript fiddle 以按照代码注释中的问题工作。
这是我的 updated version.
我已经更改了您的 class 结构,因此不需要任何静态变量,在这种情况下,这似乎是绕过糟糕设计的 hack。
您已将模型结构创建为:
- 一次购买有很多产品
- 一个产品有很多项目
但是您的 post 数据格式要求表明:
- 一次购买有很多商品
- 一件商品属于一件产品(通过参考 ID)
为了避免这种不一致,我将产品的序列化数据展平,以便 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)
是 shorthand 用于展平一层深度的嵌套数组(取自此 answer)。
现在每个产品实例都会在其自身上保存一组项目,而不是静态地保存在 class 上。
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)
我认为对这个 class 结构进行更好的重新设计是删除 Product
或 Item
class,因为只有一个产品 ID,并且据我所知,项目就像一个计数器,显示正在购买多少单位的产品。您可以在产品上保留一个整数值,而不是为此使用 class:
作为 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
输出现在看起来不同,但更干净:
新:
{
"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"
}
]
}
但这可能不适用于您当前的后端实现,具体取决于当前处理重复项的方式,因此如果无法更改任何遗留约束,请忽略最后一部分。
最后,我想补充一点,这种将事件侦听器和逻辑附加到 DOM 的 "object-oriented" 方法比加载时执行的典型 jquery 函数更加结构化。但我过去曾使用过它,同时保持 DOM 结构和代码更新是一件痛苦的事情,而且经常会因为一个代码的更改没有反映到另一个而导致错误。
作为替代方案,我强烈建议研究 reactjs 或类似的 DOM 抽象类型库。这些允许您将您的逻辑与它们所依赖的视图元素紧密耦合。
虽然通常与 JSX 一起使用,但它与 Coffeescript 结合得很好,但是这方面的资源很少。 Arkency 也写了一篇关于 react + coffeescript and I wrote a short post comparing coffeescript to jsx 的好博客。
题底有javascript
和coffescript
jsfiddle。
两者 fiddle 都包含 需要按特定顺序阅读的解释性注释 ,它 将值打印到 console
当你点击 product
或 submit div
时,另外我给你这个问题的基本解释。
- 我有 3 个 Javascript 类
Purchase
、Product
和Item
- 一个
Purchase
有很多Products
,一个Product
有很多Items
Purchase
对象在$('submit')
上设置了一个click event handler
,onClick()
将 postitems
数据发送到我的后端 api这是从 my backend api
接受的data
格式{ 'purchase' => { 'items_attributes' => { '0' => { 'purchase_id' => '1' }, '1' => { 'purchase_id' => '2' } } } }
My coffeescript jsfiddle is at the following link
点击下面打开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;
}
<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>
<div id="submit">Create Purchase</div>
</ul>
<h4>check logs by opening the console</h4>
在我编写开源代码时,您可以查看我的 commit history, the specific commit 并 fork 项目
我是 Active Model Serializer gem 的粉丝,它现在是 Rails 的一部分。我会尝试通过向所有 classes 添加一个序列化方法,将这种模式扩展到您的 coffeescript 中,并在您将数据传递到服务器时调用这些方法。
我不确定你对 Item
class 的计划,所以这里有一个使用建议的 serialize
方法的简单模型:
class Item
constructor: (@purchase, @product, @quantity) ->
serialize: =>
purchase_id: @purchase.id.toString()
product_id: @product.id.toString()
quantity: parseInt(@quantity)
鉴于您的购买 class 将有一个 @items
的数组,那么 Purchase
的 serialize
方法将如下所示:
serialize: =>
items_attributes: (item.serialize() for item in @items)
然后你的 ajax post 将使用 serialize
方法:
$.ajax
url: "/items"
method: "POST"
dataType: "json"
data:
purchase: @serialize()
error: (jqXHR, textStatus, errorThrown) ->
success: (data, textStatus, jqXHR) ->
那么你应该得到 JSON post 的
正文'purchase' => {
'items_attributes' => [
{
'purchase_id' => '1'
},
{
'purchase_id' => '2'
}
]
}
您可以通过强大的参数在您的 rails 控制器中使用它:
params.require(:purchase).permit(item_attributes: [:purchase_id])
初始化时,您可以简单地将事件绑定到 Purchase
对象中。
this.submit.click(function() {
return console.log(Product.serialize());
});
工作代码段: 我已经注释掉 Product
的 onSubmit
。
(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>
我已经更新了您的 coffeescript fiddle 以按照代码注释中的问题工作。
这是我的 updated version.
我已经更改了您的 class 结构,因此不需要任何静态变量,在这种情况下,这似乎是绕过糟糕设计的 hack。
您已将模型结构创建为:
- 一次购买有很多产品
- 一个产品有很多项目
但是您的 post 数据格式要求表明:
- 一次购买有很多商品
- 一件商品属于一件产品(通过参考 ID)
为了避免这种不一致,我将产品的序列化数据展平,以便 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)
是 shorthand 用于展平一层深度的嵌套数组(取自此 answer)。
现在每个产品实例都会在其自身上保存一组项目,而不是静态地保存在 class 上。
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)
我认为对这个 class 结构进行更好的重新设计是删除 Product
或 Item
class,因为只有一个产品 ID,并且据我所知,项目就像一个计数器,显示正在购买多少单位的产品。您可以在产品上保留一个整数值,而不是为此使用 class:
作为 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
输出现在看起来不同,但更干净:
新:
{
"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"
}
]
}
但这可能不适用于您当前的后端实现,具体取决于当前处理重复项的方式,因此如果无法更改任何遗留约束,请忽略最后一部分。
最后,我想补充一点,这种将事件侦听器和逻辑附加到 DOM 的 "object-oriented" 方法比加载时执行的典型 jquery 函数更加结构化。但我过去曾使用过它,同时保持 DOM 结构和代码更新是一件痛苦的事情,而且经常会因为一个代码的更改没有反映到另一个而导致错误。
作为替代方案,我强烈建议研究 reactjs 或类似的 DOM 抽象类型库。这些允许您将您的逻辑与它们所依赖的视图元素紧密耦合。
虽然通常与 JSX 一起使用,但它与 Coffeescript 结合得很好,但是这方面的资源很少。 Arkency 也写了一篇关于 react + coffeescript and I wrote a short post comparing coffeescript to jsx 的好博客。