Introduction
Using foreach
data binding in knockout, we can easily bind data to a table. But the trike part comes when we need to use rowspan
in table
. An example could be as simple as below or could be even more complex one, when we are using rowspan
s to make data more presentable to the user.
Background
To accomplish our goal, we are going to use:
- Regular
foreach
data binding - Container less data binding to bind additional data
attr
data binding to specify the rowspan
Using the Code
For a quick preview, checkout http://jsfiddle.net/y8Gzt/.
Let’s specify some models which we are going to use in the model binding.
function Subject(name, mark) {
this.name = name;
this.mark = parseFloat(mark).toFixed(2);
}
function Student(name, regNo, subjects) {
this.name = name;
this.regNo = regNo;
this.subjects = subjects;
this.totalMark = function() {
var total = parseFloat(0);
$.each(subjects, function(i) {
total += parseFloat(subjects[i].mark);
});
return parseFloat(total).toFixed(2);
};
this.average = function() {
var avg = this.totalMark() / parseFloat(subjects.length);
return parseFloat(avg).toFixed(2);
};
this.rowSpan = subjects.length + 1; }
Here is our view model where self.init()
is populating the binding data.
function ViewModel() {
var self = this;
self.students = ko.observableArray([]);
self.init = function () {
var subjects = [
new Subject('Math', 50),
new Subject('Physics', 50),
new Subject('Chemistry', 50)
];
var students = [
new Student('Alan', 'Std-01', subjects),
new Student('Scott', 'Std-01', subjects),
new Student('John', 'Std-01', subjects)
];
self.students(students);
};
}
Finally, we are going to apply binding for the view model:
$(document).ready(function() {
var vm = new ViewModel();
vm.init(); ko.applyBindings(vm);
});
Now let’s hook this view model to HTML table.
<tbody data-bind="foreach: students">
- is just the regular binding <!-- ko foreach: subjects --> <!-- /ko -->
- is the important container less binding for row murching attr: { rowspan: rowSpan }
- is the rowspan
binding to use rowspan
in the table
<table>
<thead>
<tr>
<th>Student Name</th>
<th>Student Reg No.</th>
<th>Subject Name</th>
<th>Obtained Marks</th>
<th>Total Marks</th>
<th>Average Marks</th>
</tr>
</thead>
<tbody data-bind="foreach: students">
<tr>
<td data-bind="text: name, attr: { rowspan: rowSpan }"></td>
<td data-bind="text: regNo, attr: { rowspan: rowSpan }"></td>
<!--important to user class hidden to hide the cell-->
<td class="hidden"></td>
<td class="hidden"></td>
<td data-bind="text: totalMark(), attr: { rowspan: rowSpan }"></td>
<td data-bind="text: average(), attr: { rowspan: rowSpan }"></td>
</tr>
<!--knockout Containerless Binding-->
<!-- ko foreach: subjects -->
<tr>
<!--important to user class hiddenTop to hide top border-->
<td class="hiddenTop" data-bind="text: name"></td>
<td class="hiddenTop" data-bind="text: mark"></td>
</tr>
<!-- /ko -->
</tbody>
</table>
Now in the header section, let’s specify some styles so that our table may have a fresh and clean look. Here are some styles that are really important to use:
td.hidden
- hides unnecessary cells td.hiddenTop
- hides unnecessary top border from cells
<style>
table {
border: 1px solid black;
border-collapse: collapse;
width: 700px;
}
thead {
font-size: 15px;
font-weight: bold
}
tbody {
font-size: 15px;
font-weight: normal
}
td, th {
border: 1px solid black;
text-align: center;
padding: 10px;
}
td.hidden {
border-width: 0px;
padding: 0px;
}
td.hiddenTop {
border-top-width: 0px;
}
</style>
Find the VS 2010 solution in the attachment.