I am trying to delete an item from a list. I am using knockout.js with the mapping plugin. My code looks like this:
@{ var jsonData = new HtmlString(new JavaScriptSerializer().Serialize(Model));}
<script type="text/html" id="imgsList">
{{each model.Imgs}}
<div style="float:left; margin: 10px 10px 10px 0;">
<div><a href="${Filename}"><img src="${Filename}" style="width:100px;"></img></a></div>
<div data-bind="click: deleteImage">Delete</div>
</div>
{{/each}}
</script>
<script type="text/javascript">
$(function() {
//KO Setup
var viewModel = {
"model": ko.mapping.fromJS(@jsonData),
"deleteImage" : function(item) {alert(item.Filename + ' deleted.');}
}
ko.applyBindings(viewModel);
});
</script>
<div data-bind="template: 'imgsList'"></div>
Everything works as expected. A list of images shows up with delete buttons, however, when you click a button item.Filename is undefined. Thoughts?
Edit: Taken from the KNockout.js Manual: "When calling your handler, Knockout will supply the current model value as the first parameter. This is particularly useful if you’re rendering some UI for each item in a collection, and you need to know which item’s UI was clicked."
It appears that I am not getting back the Img object I am expecting. I don't know what I am getting back!
I notice there is an example of how to do this here:
http://blog.stevensanderson.com/2011/12/21/knockout-2-0-0-released/
Check out the 4. Cleaner event handling section where Steve shows an example of an item being deleted from a list.
<h3>Products</h3>
<ul data-bind="foreach: products">
<li>
<strong data-bind="text: name"></strong>
<button data-bind="click: $parent.removeProduct">Delete</button>
</li>
</ul>
Javascript:
function appViewModel() {
var self = this;
self.products = ko.observableArray([
{ name: "XBox" },
{ name: "PlayStation" },
{ name: "Banana" },
{ name: "Wii" }
]);
self.removeProduct = function(product) {
self.products.remove(product);
}
};
ko.applyBindings(new appViewModel());
But take into account that the above example is for KnockoutJS 2.0 which is the latest release.
When you use {{each}} syntax in jQuery Templates, the data context is whatever the overall template is bound against. In your case, that is the entire view model.
A few options:
1- you can use your current code and pass the item that you are "eaching" on to the function like ( http://jsfiddle.net/rniemeyer/qB9tp/1/ ):
<div data-bind="click: function() { $root.deleteImage($value); }">Delete</div>
Using an anomymous function in the data-bind is pretty ugly though. There are better options.
2- you can use the foreach
parameter of the template binding, which works with jQuery Templates and is more efficient than {{each}} like ( http://jsfiddle.net/rniemeyer/qB9tp/2/ ):
<script type="text/html" id="imgsList">
<div style="float:left; margin: 10px 10px 10px 0;">
<div>
<a href="${Filename}">${Filename}</a>
</div>
<div data-bind="click: $root.deleteImage">Delete</div>
</div>
</script>
<div data-bind="template: { name: 'imgsList', foreach: model.Imgs }"></div>
Now, the context of the template is the individual image object and calling $root.deleteImage
will pass it as the first argument.
3- Since, the jQuery Templates plugin is deprecated and Knockout now supports native templates, you might want to choose removing your dependency on the jQuery Templates plugin. You could still use a named template (just need to replace any jQuery Templates syntax with data-bind attributes) like: http://jsfiddle.net/rniemeyer/qB9tp/3/ or even remove the template and just go with the foreach
control-flow binding like: http://jsfiddle.net/rniemeyer/qB9tp/4/
<div data-bind="foreach: model.Imgs">
<div style="float:left; margin: 10px 10px 10px 0;">
<div>
<a data-bind="text: Filename, attr: { href: Filename }"></a>
</div>
<div data-bind="click: $root.deleteImage">Delete</div>
</div>
</div>
4- While I prefer option #3, you could even choose to use event delegation and attach a "live" handler like: http://jsfiddle.net/rniemeyer/qB9tp/5/
$("#main").on("click", ".del", function() {
var data = ko.dataFor(this);
viewModel.deleteImage(data);
});
This can be especially beneficial if you would be attaching a large number of the same handlers via the click
binding (like in a grid).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With