Reputation: 5001
Read the data-product-id
from DOM
added by KnockoutJS.
I have the following markup:
<!-- ko foreach: Summary.products -->
<li data-bind="attr: { 'data-product-id': id }">
<div class="product-summary-actions float-right">
<button class="btn btn-danger btn-mini remove-item">
<i class="icon-remove"></i>
</button>
</div>
<div class="product-summary-quantity">
<h6 data-bind="text: infoComposition"></h6>
</div>
<div class="product-summary-description">
<p data-bind="text: name"></p>
</div>
</li>
<!-- /ko -->
As you can see, there is a data-bind at the first line after the comment with attr
binding. Look:
<li data-bind="attr: { 'data-product-id': id }">
When I check my DOM using Chrome's console, I have the following:
<li data-bind="attr: { 'data-product-id': id }" data-product-id="1">...</li>
As you could see, data-product-id
was successfully applied. But, when I have to make an interation with him, no success.
There is a function in my application that is responsible to check if an item exists on my Products Summary, and the following loop does this:
$(element).each(function () {
var $productId = $(this).closest("li").data("product-id"),
$match = $(".summary")
.find("li[data-product-id=" + $productId + "]").length > 0;
console.log($match);
});
Always the return is false. In other words, seems that jQuery doesn't consider the data-product-id
generated by KnockoutJS because if I manually add the data-product-id
attribute to my items (like the following markup), all works fine.
<!-- ko foreach: Summary.products -->
<li data-product-id="1">
<div class="product-summary-actions float-right">
<button class="btn btn-danger btn-mini remove-item">
<i class="icon-remove"></i>
</button>
</div>
<div class="product-summary-quantity">
<h6 data-bind="text: infoComposition"></h6>
</div>
<div class="product-summary-description">
<p data-bind="text: name"></p>
</div>
</li>
<!-- /ko -->
My necessary HTML:
<button class="btn btn-success btn-small add"
title="Add to comparison list">
<i data-bind="attr: { class: ProductLayout.existsAtSummary($element) ?
'icon-minus' : 'icon-plus' }">
</i>
</button>
My JS:
function ProductLayoutViewModel() {
var self = this;
self.itemQuantity = ko.observable("");
self.itemQuantityValid = ko.computed(function () {
var q = self.itemQuantity();
return q != "0" && q != "00" && q != null && q != "";
}, this);
self.existsAtSummary = function (element) {
$(element).each(function () {
$productId = $(this).closest("li").data("product-id");
$match = $(".summary")
.find("li[data-product-id=" + $productId + "]").length;
if (!$match)
return true;
else
return false;
});
});
};
ViewModel = {
Summary: new SummaryViewModel(),
ProductLayout: new ProductLayoutViewModel()
};
$.ajax({
url: "/ProductsSummary/List?output=json",
dataType: "json",
success: function (data) {
var mappingOptions = {
create: function (options) {
return (new (function () {
this.finalMeasure = ko.computed(function () {
return this.quantity() > 1 ?
this.measure() + "s" : this.measure();
}, this);
this.infoComposition = ko.computed(function () {
return this.quantity() + ' ' + this.finalMeasure();
}, this);
ko.mapping.fromJS(options.data, {}, this);
})());
}
};
ViewModel.Summary.products = ko.mapping.fromJS(data, mappingOptions);
ko.applyBindings(ViewModel);
}
});
Someone knows how can I solve this problem? Thanks!
Upvotes: 2
Views: 2730
Reputation: 271
looks like you have a timing problem there.
Please note, that knockoutjs first goes through your js models and then filles the view with content. So if you are testing or in your case iteration over some DOM structure, you run straight into a timing error.
Try to rethink what you are doing there. As knockoutjs is providing the html with data, you already have all data in your js code. To me this looks like a work-a-round.
eg: Your template:
<!-- ko foreach: Summary.products -->
<li data-bind="attr: { 'data-product-id': id }">
</li>
<!-- /ko -->
in knockoutjs you already have a list of products and the particular product-id. So all you need to do is to check the length of your list? Or do I miss something there?
If I get it wrong, and you just want to trigger another javascript which is doing something when your list is rendered, try to trigger some event when knockoutjs has filled your page with content. Or trigger your javascript after the dom is ready.
Upvotes: 1
Reputation: 8987
Accordling to this fiddle, it should work.
Are sure you execute the $(element).each after applied bindings ?
JS :
var vm = { id:'myID-product'};
ko.applyBindings(vm);
var element = $("#myID").data("product-id");
console.log(element);
View :
<li data-bind="attr: { 'data-product-id': id }" id="myID">
So, I think you should :
Upvotes: 1
Reputation: 9167
I'm guessing you're doing the $.each before your DOM is ready (before Knockout has made its modifications).
Take the each out of your ViewModel and put it in this code-block:
$(function() {
$(element).each(function () {
var $productId = $(this).closest("li").data("product-id"),
$match = $(".summary")
.find("li[data-product-id=" + $productId + "]").length > 0;
console.log($match);
});
});
Upvotes: 1