Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Knockout to Populate Bootstrap Rows and Spans

Well essentially I'm trying to populate a Bootstrap template via Knockout and a JSON object.

Bootstrap scaffolding:

<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>
...

Here is the Knockout code we're using:

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?

like image 926
JustinBull Avatar asked Aug 25 '12 19:08

JustinBull


2 Answers

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>
like image 175
rkhayrov Avatar answered Oct 23 '22 06:10

rkhayrov


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.

like image 22
gonz Avatar answered Oct 23 '22 05:10

gonz