I want to build my own listView with selectedItem and Itemsource.
I have started a jsFiddle http://jsfiddle.net/andersb79/53xRL/1/
Can you please help me in the right direction about how I should build this.
I havn't seen a bindingHanlder that do this.
What I want is something like this.
data-bind="myListView : { items : comments, selectedItem : selectedComment}"
In your custom binding you need to:
- Create an element for each of the items in the list
- Append that element to the parent
- Add a click handler to each element that sets the selectedItem on the original view model
I've also highlighted the current selected item for clarity.
ko.bindingHandlers.myListView = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
//get the list of items
items = value.items(),
//get a reference to the selected item observable
selectedItem = value.selectedItem,
//get a jQuery reference to the element
$element = $(element),
//get the currently selected item
currentSelected = selectedItem();
//clear the parent of any existing children
$element.html("");
for (var index = 0; index < items.length; index++) {
(function() {
//get the list of items
var item = ko.utils.unwrapObservable(items[index]),
//create a child element with a click handler
$childElement = $("<li>")
.text(item.id() + " " + item.text())
.click(function() {
//remove selected class on all siblings
$(this).siblings().removeClass("selected");
//add selected class on this item
$(this).addClass("selected");
//set the observable 'selected item' property on the source view model
selectedItem(item);
});
//add the selected class if this item is the current selected item
if (item == currentSelected) {
$childElement.addClass("selected");
}
//add the child to the parent
$element.append($childElement);
})();
}
}
};
Here's a working example
Note: As I am using the update
instead of the init
method it will work when updating the list of comments
Update
If you want to use the contents of the original DIV as the template for each created item, you need two additional steps:
- Grab the original content of the element when initialized and use that in place of the hard-coded content above
Apply bindings to the created element before adding it to the DOM
ko.bindingHandlers.myListView = {
init: function(element) {
var $element = $(element),
originalContent = $element.html();
$element.data("original-content", originalContent);
return { controlsDescendantBindings: true }
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
//get the list of items
items = value.items(),
//get a reference to the selected item observable
selectedItem = value.selectedItem,
//get a jQuery reference to the element
$element = $(element),
//get the currently selected item
currentSelected = selectedItem(),
//get the current content of the element
elementContent = $element.data("original-content");
$element.html("");
for (var index = 0; index < items.length; index++) {
(function() {
//get the list of items
var item = ko.utils.unwrapObservable(items[index]),
//create a child element with a click handler
$childElement = $(elementContent)
.click(function() {
//remove selected class on all siblings
$(this).siblings().removeClass("selected");
//add selected class on this item
$(this).addClass("selected");
//set the observable 'selected item' property on the source view model
selectedItem(item);
});
ko.applyBindings(item, $childElement[0]);
//add the selected class if this item is the current selected item
if (item == currentSelected) {
$childElement.addClass("selected");
}
//add the child to the parent
$element.append($childElement);
})();
}
}
};
Note that we have to prevent the content of the div being processed by knockout by returning { controlsDescendantBindings: true }
from our init
method.
Here is an updated example