Question

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>        
Was it helpful?

Solution

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>    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top