aman
aman

Reputation: 6242

Knockout JS: Dynamically adding and removing table row

I am using knockout js here.

I have a HTML table and the table has 4 columns. I have button to add a row to table and then remove button against each row to delete it. HTML table as below.

 <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>
            </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><span class="input-small" data-bind="value: firstName" /></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: removeRow" class="btn btn-danger" />
                </td>
            </tr>
        </tbody>
    </table>
    <input type="button" value="Add Row" class="btn btn-primary" data-bind="click: addRow" />   

My knockout as:

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

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


    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);

})();

When I click add row, it adds the first row but gives me console error:

knockout.js:73 Uncaught ReferenceError: Unable to process binding "click: >function (){return removeRow }" Message: removeRow is not defined

When I click add row again it gives me another console error:

Uncaught Error: You cannot apply bindings multiple times to the same element.

And when I click removeRow nothing happens.

When I comment out the code for removeRow, I am able to add a new row. Not sure where I am going wrong.

Here is my jsfiddle:

https://jsfiddle.net/aman1981/nz2dtud6/2/

Upvotes: 2

Views: 2092

Answers (2)

haim770
haim770

Reputation: 49095

Since your <tbody> defines a new scope by using a foreach: items binding, you need to use $parent.removeRow to refer to the method.

<input data-bind="click: $parent.removeRow" type="button" value="Remove Row" />

See BindingContext

Upvotes: 1

Paul
Paul

Reputation: 2076

When using the data binding foreach the context changes to the context of its childs. To access the context of the parent, you need to add $parent to access removeRow

<td>
    <input type="button" value="Remove Row" data-bind="click: $parent.removeRow" class="btn btn-danger" />
</td>

Upvotes: 2

Related Questions