Search code examples
twitter-bootstrapknockout.jsknockout-2.0

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?


Solution

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