Does anybody know how I'd accomplish the following pattern with knockoutJS?
I use this pattern all the time in apache flex and want to see if I can mimic it.
I am unsure how to replace an observable with another. Any help/thoughts much appreciated.
//model
myViewModel = {
items : ko.observableArray(),
selected_item : ko.observable()
}
//view
<h3 data-bind="text : myViewModel.selected_item.name"> </h3>
<ul>
<!-- ko foreach: myViewModel.items -->
<li data-bind="text : name"/>
<!-- /ko -->
</ul>
//logic
$('li').click(function(e){
//check ko.dataFor(this) has different id from that of myViewModel.selected_item
//if id different
//set myViewModel.selected_item to ko.dataFor(this)
//rejoice as h3 text changes
})
You are on the right track. There are a few patterns that you can use to choose a selected item. If you want to attach a click handler unobtrusively, as you have above, then your best bet would be to use a delegated handler, so that you are set to handle changes to your observableArray. A delegated handler will be able to handle new elements being added.
So, you could do something like:
$("#content").on("click", "li", function(e) {
var item = ko.dataFor(this),
current = myViewModel.selected_item;
if (!current() || item.id !== current().id) {
current(item);
}
});
Here is a sample: http://jsfiddle.net/rniemeyer/hBUBN/
When you bind to your h3
, since selected_item
can be null you would need to protect yourself by wrapping it in a with
block (in the example), calling a function that handles null, or doing it in the binding like (data-bind="text: myViewModel.selected_item() ? myViewModel.selected_item().id : 'unknown'"
). To keep it clean, this logic can be put in a function and you can call the function from your data-bind or using with
prevents this from being an issue (although it renders nothing when it is null).
Otherwise, you can even just do this:
<!-- ko foreach: myViewModel.items -->
<li data-bind="text : name, click: $parent.selected_item"></li>
<!-- /ko -->
The click
(and event
) binding in KO 2.0 pass the current data as the first argument. You can use the $parent
variable to access one scope level up (or $root
to get to the base level). Observables are functions and you set their value by passing the new value as the first argument. So, doing $parent.selected_item
here will call the observable function passing your data as the first argument. So, it will set your selected value to the proper item.
Sample: http://jsfiddle.net/rniemeyer/gemug/