Well essentially I'm trying to populate a Bootstrap template via Knockout and a JSON object.
<div class="row-fluid">
<div class="span4">
<h1>App Title</h1>
<p>App Description</p>
</div>
<div class="span4">
<h1>App Title</h1>
<p>App Description</p>
</div>
<div class="span4">
<h1>App Title</h1>
<p>App Description</p>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<h1>App Title</h1>
<p>App Description</p>
</div>
<div class="span4">
<h1>App Title</h1>
<p>App Description</p>
</div>
<div class="span4">
<h1>App Title</h1>
<p>App Description</p>
</div>
</div>
...
var viewModel;
$.get('AppData.json', function (data) {
jsonData = $.parseJSON(data);
viewModel = ko.mapping.fromJS(jsonData);
var apps = viewModel.Apps();
ko.applyBindings(viewModel);
});
The problem is that I cannot get Knockout to inject the </div><div class="row-fluid">
required after running a knockout conditional of index modulo 3... I'm assuming because those <div>
tags are dangling / not closed.
In short, how do I get viewModel.Apps();
's array of objects to fit within the above Bootstrap scaffolding?
Make a computed observable which slices apps
observable/observable array into arrays of three elements, and then bind some root element to it with foreach
binding. Something like this.
Observable:
viewModel.appRows = ko.computed(function() {
var apps = this.Apps();
var result = [];
for (var i = 0; i < apps.length; i += 3) {
var row = [];
for (var j = 0; j < 3; ++j) {
if (apps[i + j]) {
row.push(apps[i + j]);
}
}
result.push(row);
}
return result;
}, viewModel);
Markup:
<div class="container" data-bind="foreach: appRows">
<div class="row-fluid" data-bind="foreach: $data">
<div class="span4">
<h1 data-bind="text: title"></h1>
<p data-bind="text: description"></p>
</div>
</div>
</div>
I had to solve a very similar problem myself: rendering bootstrap grids with an observable array but with bootstrap v3 and ko v3.0. I'll leave my solution here for future reference.
I'm using a plain function instead of a computed one, since binding are implemented using them by default (see RP Niemeyer answer here https://stackoverflow.com/a/6712389/323751)
In my View Model:
this.partitioned = function (observableArray, count) {
var rows, partIdx, i, j, arr;
arr = observableArray();
rows = [];
for (i = 0, partIdx = 0; i < arr.length; i += count, partIdx += 1) {
rows[partIdx] = [];
for (j = 0; j < count; j += 1) {
if (i + j >= arr.length) {
break;
}
rows[partIdx].push(arr[i + j]);
}
}
return rows;
};
Template:
<div data-bind="foreach: partitioned(userProjects, 3)">
<div class="row"
data-bind="template: { name: 'projectCell', foreach: $data }"></div>
</div>
<script id="projectCell" type="text/html">
<div class="col-md-4" data-bind="text: name"></div>
</script>
Hope somebody find this useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With