I am attempting to provide a mechanism for editing entries in an observable array. The display would have two sections. The first is the array entries displaying a limited number of fields, and the second would allow the user to edit all fields for a selected entry.
To do this, I provided a double click event for each displayed entry of the array, the event would use the index and a computed observable to select a slice of the array. The hope is that I can use this method to edit array entries.
The problem is that the computed does not seem to work, and I cannot find a method that does what I want. I created a fiddle illustrating my sorry attempt to make this work
http://jsfiddle.net/rscidmore/YrsCj/
Your help would be appreciated.
My code looks like this:
var contactModel = function() {
var self = this;
self.id = ko.observable();
self.name = ko.observable();
self.addresses= ko.observableArray();
self.selectIndex = ko.observable(0);
self.selectedAddress = ko.computed(function() {
return self.addresses.slice(self.selectIndex ());
});
};
var addressModel = function(id, type, address) {
var self = this;
self.id = ko.observable(id);
self.type = ko.observable(type);
self.address = ko.observable(address);
};
var contact = new contactModel();
contact.id = 1;
contact.name = 'John Smith';
var addr = new addressModel('1', 'billing', '123 Your Street')
contact.addresses.push(addr);
addr = new addressModel('2', 'shipping', 'ABC Your Avenue')
contact.addresses.push(addr);
addr = new addressModel('3', 'home', 'XYZ Your Drive')
contact.addresses.push(addr);
ko.applyBindings(contact);
And my html looks like this:
<!DOCTYPE html>
<html>
<head>
</head>
<body class='ui-widget'>
<div class='contactInfo'>
<span class='id' data-bind="text: id"></span> :
<span class='dat1' data-bind="text: name"></span>
</div>
<div class='container'>
<!-- ko foreach: addresses -->
<div class='addrs' data-bind="event: { dblclick: function() {
$parent.selectIndex($index());}}">
<span class='id' data-bind="text: id"></span> :
<span class='dat1' data-bind="text: type"></span>
<span class='dat2' data-bind="text: address"></span>
</div>
<!-- /ko -->
</div>
<div class='contactInfo'>
<span class='id' data-bind="text: selectIndex"></span> :
<input class='dat2' type='text' data-bind="value: selectedAddress.address" />
</div>
</body>
</html>
Typically what you would want to do is represent your "selected" item as an observable. Handlers hooked up through the event
/ click
binding receive the current data item as the first argument. This can be used to populate your "selected" observable directly.
So, it would be like:
var ContactModel = function() {
this.id = ko.observable();
this.name = ko.observable();
this.addresses= ko.observableArray();
this.selectedAddress = ko.observable();
};
Then, you can bind against it like:
<div class='container'>
<!-- ko foreach: addresses -->
<div class='addrs' data-bind="event: { dblclick: $parent.selectedAddress }">
<span class='id' data-bind="text: id"></span> :
<span class='dat1' data-bind="text: type"></span>
<span class='dat2' data-bind="text: address"></span>
</div>
<!-- /ko -->
</div>
Note that you could create a function called "selectAddress" on your contact model and populate selectedAddress
with the item passed as the first argument. However, since an observable is already a function and populates its value using the first argument passed to it, in the sample above I bound dblclick
directly against the observable.
Sample here: http://jsfiddle.net/rniemeyer/2DmUf/
Sample with a function rather than bound directly against an observable here: http://jsfiddle.net/rniemeyer/mDKGV/ (just to help make it clear)
A handy thing to do with a "selected" observable, is to use the with
binding around an area, so it will re-render whenever you change to a new selected item and protect against when the item is null.
<div class='contactInfo' data-bind="with: selectedAddress">
<input class='dat2' type='text' data-bind="value: address" />
</div>