todd.pund
todd.pund

Reputation: 689

Knockout.js - text input on each row of foreach table, should only edit field on that row

I have created a fiddle of the following code here.

I have created a table using a knockout foreach. Each row can be removed by an inline button on that row. Rows can be dynamically added, and a label can be edited in real time by selecting the row then using the textbox.

What I am wanting to do is move that one textbox into multiple textboxes, one on each row that edits that row alone. Help is appreciated.

Viewmodel:

function Model() {
    var self = this;
    this.tasks = ko.observableArray();
    this.container = ko.observableArray();
    this.tid = ko.observable(0);
    this.addTask = function(id, content, content2) {
        var obj = {
            id: ko.observable(id),
            content: ko.observable(content),
            content2: ko.observable(content2),
            selected: ko.observable(false)
        };   
        self.tid(id);
        self.tasks.push(obj);        
    };
    this.addNewTask = function() {
      var obj = {
        id: self.tid() + 1,
        content: "New Task",
        content2: "New Service",
        selected: ko.observable(false)
      }
      self.tid(self.tid() + 1);
      self.tasks.push(obj);

    }
    this.selectedIndex = ko.observable(0);
    this.selectTask = function(task) {
        self.selectedIndex(self.tasks.indexOf(task));
    };
    self.removeTask = function(task) {
      self.tasks.remove(task)
    };
};

var myModel = new Model();
myModel.addTask(1, "Task 1", "Service 1");
myModel.addTask(2, "Task 2", "Service 2");
myModel.addTask(3, "Task 3", "Service 3");

ko.applyBindings(myModel);

View:

<div data-bind="foreach: tasks">
<table cellpadding="0" cellspacing="0">
<tr>
  <td>
  <label class="wrapper" data-bind="text: content, click: $root.selectTask, css: {'selected': $root.selectedIndex() === $index()}" />
  </td>
  <td>-</td>
  <td>
    <span class="wrapper" data-bind="text: content2, click: $root.selectTask, css: {'selected': $root.selectedIndex() === $index()}"></span>
  </td>
  <td> <input type="button" value="remove" data-bind="click: $parent.removeTask"></td>

</tr>
</table>    
</div>
<br />
Add New Task: &nbsp;<input type="button" value="add" data-bind="click: addNewTask"><br /><br />
<label>Edit Task:</label> <input type="text" data-bind="value: tasks()[selectedIndex()].content2, valueUpdate: 'afterkeydown'">

Upvotes: 0

Views: 1319

Answers (2)

Lo&#239;c
Lo&#239;c

Reputation: 141

First of all, if you want to edit your newly created lines, you have to make ko.observable its values :

this.addNewTask = function() {
    var obj = {
    id: ko.observable(self.tid() + 1),
    content: ko.observable("New Task"),
    content2: ko.observable("New Service"),
    selected: ko.observable(false)
  } ....

Then, to have a textbox on each line, replace :

<span class="wrapper" data-bind="text: content2, click: $root.selectTask, css: {'selected': $root.selectedIndex() === $index()}"></span>

with :

<input type="text" data-bind="value: content2, valueUpdate: 'afterkeydown'">

To go further move your data-bind="foreach: tasks" to the . You'll repeatlines and not tables for each task.

Upvotes: 0

user3297291
user3297291

Reputation: 23372

You can move the <input> to its own <td> in the row.

You're currently binding the input to the selected item; on its new location, you can just bind it to the bindingContext:

<td>
  <label>Edit Task:</label> <input type="text" data-bind="value: content2, valueUpdate: 'afterkeydown'">
</td>

For it to work on new items, you'll have to make sure those get observable properties as well. (I've also changed this in the updated fiddle)

https://jsfiddle.net/90w1pagr/

Upvotes: 1

Related Questions