Radio Buttons in Knockout Simplegrid Using Custom Grid Template

This is a follow on from a previous post First Look At Knockout Paged Simplegrid which I suggest you read as we will be building on the code from that post.

So now we want to extend the SimpleGrid and apply an action to each row.  In the table below I can select Approve or Reject and click the button to perform some like update the database.

knockout simple grid with controls

The first thing that changes from previous code is the definition of the columns. Notice how an Action column is now defined, data element of the column is a function.  This function will be the bound to the button click event that is executed on each row.

this.gridViewModel = new ko.simpleGrid.viewModel({
    data: this.items,
    columns: [
        { headerText: "Person ID", rowText: "employeeid" },
        { headerText: "Title", rowText: "title" },
        { headerText: "Forename", rowText: "forename" },
        { headerText: "Surname", rowText: "surname" },
        {
            headerText: "Action", rowText: {
                action: function (item) {
                    return function () {
                        alert(item.employeeid);
                        alert(item.selected());
                    }
                }
            }
        }
    ],
    pageSize: 5
});

There is a new item in the data called selected that is marked as observable. This item will be bound to the radio button group of each row. To simplify things I have also removed the startdate data item from the previous example as it was not being used in this example.

var gridData = [
    { employeeid: 1000, title: "Mr", forename: "Fred", surname: "Flinstone", selected: ko.observable() },
    { employeeid: 1001, title: "Mr", forename: "Barney", surname: "Rubble", selected: ko.observable() },
    { employeeid: 1002, title: "Ms", forename: "Belinda", surname: "Mason", selected: ko.observable() },
    { employeeid: 1003, title: "Mrs", forename: "Mary", surname: "Moore", selected: ko.observable() },
    { employeeid: 1004, title: "Mr", forename: "Harry", surname: "Hill", selected: ko.observable() },
    { employeeid: 1005, title: "Dr", forename: "Carl", surname: "Flowers", selected: ko.observable() },
    { employeeid: 1006, title: "Miss", forename: "Jenna", surname: "Pray", selected: ko.observable() },
    { employeeid: 1007, title: "Mr", forename: "John", surname: "Doe", selected: ko.observable() },
    { employeeid: 1008, title: "Mrs", forename: "Jane", surname: "Smith", selected: ko.observable() },
    { employeeid: 1009, title: "Mrs", forename: "Helen", surname: "Jones", selected: ko.observable() },
    { employeeid: 1010, title: "Miss", forename: "Amy", surname: "Simmonds", selected: ko.observable() },
    { employeeid: 1011, title: "Mr", forename: "William", surname: "Parker", selected: ko.observable() }
];

This is fine if you have hard coded data in your pages, but we all know that would not normally be the case, right? If you are calling some back end method (e.g. an ajax call) to retrieve the data then you will have to add the observable item to the data manually

$.getJSON('@Url.Content("~/knockoutjs/getallemployees")', function (data) {
    for (var i = 0; i < data.length; i++) {
        data[i].selected = "ko.observable()";
    }
    ko.applyBindings(new PagedGridModel(data));
});

The final difference is how the table is constructed, a customer grid template gives a lot of control over how the grid appears.  By linking a grid template to the simplegrid div element you can practically do anything with the table you want.

Here the columns in the model are first being iterated to display the column headers in the table thead.   A table row is built for each row in the model.   The action property (which is a function) is bound to the button click event.  The selected property is bound to the radio group.  The row item is passed as a parameter to the function and because the selected property is an observable it will be set to the selected radio value.

<div data-bind='simpleGrid: gridViewModel, simpleGridTemplate: "custom_grid_template"'></div>
<script type="text/html" id="custom_grid_template">
    <table>
        <colgroup>
            <col span="4">
            <col span="1" style="width: 240px;">
         </colgroup>
        <thead>
            <tr data-bind="foreach: columns">
                <th data-bind="text: headerText"></th>
            </tr>
        </thead>
        <tbody data-bind="foreach: itemsOnCurrentPage">
            <tr data-bind="foreach: $parent.columns">
                <!--ko if: typeof rowText == 'object' && typeof rowText.action == 'function'-->
                <td>
                    <input data-bind="checked: $parent.selected, attr: { name: 'radioGroup' + $parentContext.$index() }" value="1" type="radio" /> Approve  
                    <input data-bind="checked: $parent.selected, attr: { name: 'radioGroup' + $parentContext.$index() }" value="2" type="radio" /> Reject  
                    <button data-bind="click: rowText.action($parent)">action</button>
                </td>
                <!-- /ko -->

                <!--ko ifnot: headerText == 'status' || (typeof rowText == 'object' && typeof rowText.action == 'function')-->
                    <td data-bind="text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText] "></td>
                <!--/ko-->
            </tr>
        </tbody>
    </table>
</script>

Go here to see a working demo on jsfiddle.

**Update 25th September 2014

In this post I have mentioned two ways the data can be assigned to the VieModel, hard coded and via an ajax call.  Because of the different ways the selected property is initialised as observable the value must be read differently. The only difference is the inclusion or exclusion of brackets.

When using the hard coded data method

alert(item.selected());

When making an ajax call to retrieve the data and manually

alert(item.selected);