问题
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?
回答1:
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>
回答2:
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.
回答3:
I´m using bootstrap3 and wanted to have x objects per row, where x is depending on the window size e.g on the col class.
e.g i have elements with class :
col-lg-2 col-md-2 col-sm-3 col-xs-4
so
if xs then a row has 3 items
if lg then a row has 6 items ...
I extended the answer of rkhayrov to put it to work
/* determine the current bootstrap environment */
function findBootstrapEnvironment() {
var envs = ['xs', 'sm', 'md', 'lg'];
$el = $('<div>');
$el.appendTo($('body'));
for (var i = envs.length - 1; i >= 0; i--) {
var env = envs[i];
$el.addClass('hidden-'+env);
if ($el.is(':hidden')) {
$el.remove();
return env
}
};
}
/* determine How many objs per row */
function determineObjectsPerRow(){
switch(findBootstrapEnvironment()) {
case 'xs':
return 3;
break;
case 'sm':
return 4;
break;
case 'md':
return 6;
break;
case 'lg':
return 6;
break;
}
}
var objsPerRow= determineObjectsPerRow();
for (var i = 0; i < apps.length; i += objsPerRow) {
var row = [];
for (var j = 0; j < objsPerRow; ++j) {
if (apps[i + j]) {
row.push(apps[i + j]);
}
}
result.push(row);
}
i added a dependency to width and height, which are observables So the functions will be recomputed if window is resized
var base = {
AppModel:function (data) {
var self = this;
self.data = ko.observable({
documents:ko.observableArray([]),
width:ko.observable($(window).width()),
height:ko.observable($(window).height())
});
/* calculate rows based on bootstrap environment */
self.appRows = ko.computed(function() {
self.data().height();
self.data().width();
var apps = self.data().documents();
var result = [];
var objsPerRow= determineObjectsPerRow();
for (var i = 0; i < apps.length; i += objsPerRow) {
var row = [];
for (var j = 0; j < objsPerRow; ++j) {
if (apps[i + j]) {
row.push(apps[i + j]);
}
}
result.push(row);
}
return result;
}, self);
},
};
and the resize handler
$(window).resize(function(event) {
vm.data().height($(window).height());
vm.data().width($(window).width());
});
it just works like a Charme, the rows are generated nicely for each device-width
i leave it here if anyone needs to achieve the same
来源:https://stackoverflow.com/questions/12124835/using-knockout-to-populate-bootstrap-rows-and-spans