Shahid Od
Shahid Od

Reputation: 123

Knockout JS : click-to-edit in table

first of all Im using Knockout js. So I have a table that I can add and remove rows from it dynamically, my problem is that I want to add a click-to-edit in the table for each row but it doesn't work. once I add a second row Im not enable to edit. Here is my code, you can just copy and past it JSFiddle and it will explain further what Im saying.

Here is my code:

(function () {
var ViewModel = function () {
    var self = this;

    //Empty Row
    self.items = ko.observableArray([]);

		self.editing = ko.observable(true);
    
    self.edit = function() { this.editing(true) }
    
    self.addRow = function () {
        self.items.push(new Item());            
    };

    self.removeRow = function (data) {
        self.items.remove(data);
    };        
}

var Item = function (fname, lname, address) {
    var self = this;
    self.firstName = ko.observable(fname);
    self.lastName = ko.observable(lname);
    self.address = ko.observable(address);
};

vm = new ViewModel()
ko.applyBindings(vm);

})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />

<table class="table table-bordered">
  <thead class="mbhead">
    <tr class="mbrow">
      <th>Input</th>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Address</th>
      <th>Actions</th>
    </tr>
  </thead>
  <tbody data-bind="foreach: items">
    <tr>
      <td>
        <select class="form-control common-input-text" data-bind="event: { change: $root.addNewItem }">
          <option value="">One</option>
          <option value="">Two</option>
          <option value="">Three</option>
        </select>
      </td>
      <td>
        <b data-bind="uniqueName: true,visible: !($parent.editing()), text: firstName, click: function() { $parent.editing(true) }"></b>
        <input data-bind="uniqueName: true, visible: $parent.editing, value: firstName, hasFocus: $parent.editing" />
      </td>
      <td><span class="input-small" data-bind="value: lastName" /></td>
      <td><span class="input-small" data-bind="value: address" /></td>
      <td>
        <input type="button" value="Remove Row" data-bind="click: $parent.removeRow" class="btn btn-danger" />
      </td>
    </tr>
  </tbody>
</table>
<input type="button" value="Add Row" class="btn btn-primary" data-bind="click: addRow" />

thank you for your help

Upvotes: 0

Views: 750

Answers (1)

OfirD
OfirD

Reputation: 10490

The problem lies in creating a new row that bounds an observable to hasFocus:

<input data-bind="uniqueName: true, 
                  visible: $parent.editing, 
                  value: firstName, 
                  hasFocus: $parent.editing" /> <-- the problem cause

On row creation, the previously-focused row loses focus, which causes editing to be set to false.

So the solution would be to just use the observable value (instead of bounding the observable itself):

<input data-bind="uniqueName: true, 
                  visible: $parent.editing, 
                  value: firstName, 
                  hasFocus: $parent.editing()" /> // <-- call the observable

But better yet is to add an observable into Item view model, called isFocused, and use it instead:

var Item = function (fname, lname, address) {
    var self = this;
    self.isFocused = ko.observable(true);
    // ... other observables ...
};

<input data-bind="uniqueName: true, 
                  visible: isFocused(), 
                  value: firstName, 
                  hasFocus: isFocused" />

Upvotes: 1

Related Questions