我一直在努力解决这个问题,但是无法得到我期待的结果。任何帮助将非常感激。这是jsfiddle中的代码。我在这里要做的是迭代包含可以包含多个消息的JSON产品,并呈现消息并将它们中的每一个绑定到它们自己的click功能。相反,发生的是,在运行代码时,单击功能会自动触发,单击消息链接不会执行任何操作。
<a href="#" data-bind="click: doTest">This binding works</a>
<!--The json is appended to these 2 ULs by class name-->
<ul class="product1"></ul>
<ul class="product2"></ul>
<script id="product-message-template" type="text/x-handlebars-template">
{{#each this}}
<!-- code below should be binding to alertMessage(), but fails...-->
<a href="#" data-bind="click:{{this.alertMessage}}">{{this.message}}</a>
{{/each}}
</script>
这是JS:
function init(){
var self = this;
self.view_model.doTest = function(){alert('this works')};
self.product_messages_to_vm(json, view_model);
self.render_product_messages(view_model);
}
var view_model= {};
var json = [{
"id": "product1",
"messages": [
"p1 message1",
"p1 message2"
]},{
"id": "product2",
"messages": [
"p2 message1",
"p2 message2"
]}];
//The Product object contains an observable array for messages and methods to add remove and save them
function Product(id, productMessages){
this.productId = id;
//make observableArray so when messages are removed, other messages can be notified
this.messages = ko.observableArray([]);
//iterate through messages and make them observable and add them to this product's messages
for(var i=0; i<productMessages.length; i++){
//this.messages().push(productMessages[i]);
this.messages().push(new Message(productMessages[i]));
}
}
//message object
function Message(msg){
this.message = msg;
this.alertMessage = function(){
alert(this.message);
}.bind(this);
}
//gets json messages and add them to the view model object
function product_messages_to_vm(json){
var self = this;
view_model.products = ko.observableArray([]);
_.each(json, function(product,index){
view_model.products().push( new self.Product(product.id, product.messages) );
});
}
function render_product_messages(view_model){
var self = this;
_.each(view_model.products(), function(product){
var template = Handlebars.compile($("#product-message-template").html());
var messages = product.messages();
var $message_html = $(template( messages ));
//find element with the product class name, and append the compiled template into UI
$( "." + product.productId ).append( $message_html );
});
ko.applyBindings(view_model);
}
init();
答案 0 :(得分:1)
我认为使用模板引擎对于你想要做的事情来说太复杂了。
因此,我通过使用更多的淘汰赛可能性来简化您的代码。 现在的观点是:
<div>
<div data-bind="foreach: products">
<div data-bind="foreach: messages"> <a data-bind="click: alertMessage, text : message" href="#"></a>
</div>
</div>
</div>
视图模型:
//The Product object contains an observable array for messages and methods to add remove and save them
function Product(id, productMessages) {
this.productId = id;
this.messages = ko.observableArray();
this.messages(ko.utils.arrayMap(productMessages, function (pm) {
return new Message(pm);
}));
}
//message object
function Message(msg) {
this.message = msg;
this.alertMessage = function () {
alert(this.message);
};
}
var ViewModel = function (raw) {
var self = this;
self.products = ko.utils.arrayMap(json, function (p) {
return new Product(p.id, p.messages);
});
};
答案 1 :(得分:1)
我知道这是前一段时间被问到的,但我想尝试解决它,因为我刚刚学习Knockout。
在我开始之前,为了让我理解现有的代码,我认为有必要进行一些重构。我删除了许多混淆使用的this
引用。此外,我列出了product_messages_to_vm
和render_product_messages
函数,因为这些函数未被多次调用。
您的具体问题与Handlebars模板中的以下代码段有关:
data-bind="click:{{this.alertMessage}}"
这里似乎有一些混乱,关于Knockout和Handlebars正在对这个标记做什么。 Knockout需要一个字符串,它是视图模型上方法的名称,它会将此方法绑定到元素的click事件。把手将使用模板数据中的相应值替换Handlebars表达式{{this.alertMessage}}
。
当Handlebars尝试在每个alertMessage
对象上找到message
值时,它会发现该值是一个函数; Handlebars执行函数以获取返回值(有关详细信息,请参阅this answer)。这就是您在呈现模板时收到警报的原因。由于每个消息的alertMessage
方法都不返回字符串,因此Knockout无法将click事件绑定到方法。
我们需要做的是提供一个字符串,它是我们希望绑定到click事件的处理函数的名称:
data-bind="click: alertMessage"
但是,我们遇到了问题。只有当我们的alertMessage
对象上有view_model
属性时,才能使用。我们希望调用的alertMessage
方法深深嵌套在view_model中;表示为路径,它看起来像:/ products / product [0] / messages / message [0] / alertMessage。
为了让Knockout正确绑定此方法,我们必须提供正确的路径:
data-bind="click: products()[0].messages()[0].alertMessage"
这样更好,但无论我们点击哪个链接,它都只会在第一个产品的第一条消息上调用alertMessage
。我们需要一种在模板中动态分配产品和消息索引的方法。
为此,我们必须更改模板,以便它需要product
个对象而不是messages
数组,我们必须提供产品索引:
<script id="product-message-template" type="text/x-handlebars-template">
{{#each product.messages}}
<li><a href="#" data-bind="click: products()[{{../index}}].messages()[{{@index}}].alertMessage">{{this.message}}</a></li>
{{/each}}
</script>
这要求我们更新template
来电:
_.each(view_model.products(), function (product, index) {
// TODO: Template ID should be renamed to 'product-template.
var template = Handlebars.compile($("#product-message-template").html());
var $product_html = $(template({
index: index,
product: product
}));
$("." + product.productId).append($product_html);
});
它有效!
我的重构代码如下所示:
var json = [
{
"id": "product1",
"messages": [
"p1 message1",
"p1 message2"
]
},
{
"id": "product2",
"messages": [
"p2 message1",
"p2 message2"
]
}
];
function Product (id, productMessages) {
this.productId = id;
this.messages = ko.observableArray([]);
for (var i = 0; i < productMessages.length; i++) {
this.messages().push(new Message(productMessages[i]));
}
}
function Message (msg) {
this.message = msg;
this.alertMessage = function () {
alert(this.message);
}.bind(this);
}
(function init () {
var template = Handlebars.compile($("#product-template").html());
var view_model = {
products: ko.observableArray([]),
doTest: function () {
alert('this works');
}
};
_.each(json, function (product, index) {
var next_product = new Product(product.id, product.messages);
var $product_html = $(template({
index: index,
product: next_product
}));
view_model.products().push(next_product);
$('#Products').append($product_html);
});
ko.applyBindings(view_model);
})();
更新后的HTML:
<a href="#" data-bind="click: doTest">This binding works</a>
<div id="Products"></div>
<script id="product-template" type="text/x-handlebars-template">
<ul class="{{product.productId}}">
{{#each product.messages}}
<li><a href="#" data-bind="click: products()[{{../index}}].messages()[{{@index}}].alertMessage">{{this.message}}</a></li>
{{/each}}
</ul>
</script>
可以找到一个正常运作的JS小提琴here。
尽管此代码有效,但我认为 更好的问题解决方案是使用Knockout提供的foreach binding作为suggested Damien。
我不会在这里提供此类解决方案的代码,但我将链接到working Fiddle。