I was cleaning up my model to be a bit more clear and ran into a problem during refactoring. Originally I had my model using a global variable "results" which contained the parsed Json from my API. I refactored it to actually set the model properties.
However, I'm running into a problem now where my two observable properties are empty when binding. It's probably something simple, but I'm not sure what's happening.
I'm setting my model properties here, using the JSON object returned from my API call.
function BindJsonToModel(data) {
var results = $.parseJSON(data);
model.resources(results.ResourceResults);
model.createFacets(results.ResourceFacets);
}
My ViewModel is here:
function ViewModel() {
var self = this;
self.resources = ko.observable();
self.facets = ko.observable();
self.createFacets = function (resourceFacets) {
var realModel = [];
ko.utils.arrayForEach(resourceFacets, function (item) {
realModel.push({ name: item, IsChecked: ko.observable(true) });
});
self.facets(realModel);
}
self.resourceRows = ko.computed(function () {
var rows = [],
rowIndex = 0,
itemsPerRow = 2;
var resourceList = self.resources();
if (resourceList != null) {
for (var index = 0; index < resourceList.length; index++) {
if (!rows[rowIndex]) {
rows[rowIndex] = [];
}
rows[rowIndex].push(resourceList[index]);
if (rows[rowIndex].length == itemsPerRow) {
rowIndex++;
}
}
}
return rows;
});
self.selectAll = ko.computed({
read: function () {
var firstUnchecked = ko.utils.arrayFirst(self.facets, function (item) {
return item.IsChecked() == false;
});
return firstUnchecked == null;
},
write: function (value) {
ko.utils.arrayForEach(self.facets, function (item) {
item.IsChecked(value);
});
}
});
};
var model = new ViewModel();
ko.applyBindings(model);
I also changed the way I'm generating the ViewModel by newing up on and then applying the bindings. I'm trying to follow the standards based on KO's documentation which seems to be a bit unclear between their documentation and their tutorials.
Any help would be greatly appreciated.
Some additional information. When I debug this code I see that the resourceRows
property actually iterates over the self.resources
as intended. Both the self.resources
and self.facets
contain values. It's only when I'm binding them do they come back empty.
My bind for the resources is:
<div class="container main-content">
<div class="row resource-list">
<div class="col-sm-4 col-xs-0">
</div>
<div class="col-sm-8 col-xs-12" data-bind="foreach: resourceRows">
<div class="row" data-bind="foreach: $data">
<a data-bind="attr: { href: Url }">
<div class="resource-detail">
<p data-bind="text: FriendlyName"></p>
<h6 data-bind="text: Title"></h6>
</div>
</a>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
My bind for the facets:
<script type="text/html" id="facet-checkbox-template">
<input type="checkbox" data-bind="attr: { id: name.replace(' ', '-').toLowerCase()}, checked: IsChecked " />
<label data-bind="text: name, attr: { for: name.replace(' ', '-').toLowerCase() }"></label>
<div class="clearfix"></div>
</script>
<div data-bind="template: { name: 'facet-checkbox-template', foreach: facets }"></div>
Change
self.resources = ko.observable();
self.facets = ko.observable();
to
self.resources = ko.observableArray();
self.facets = ko.observableArray();
I'm pretty sure that will fix it. A regular observable tracks the internal value, but an observableArray will track if objects are added/removed from the array. Because you're not using one, the UI will never be notified that you pushed new data into the array. Reference: http://knockoutjs.com/documentation/observableArrays.html
Edit: I found another thing that probably needs fixing:
self.selectAll = ko.computed({
read: function () {
// Below, self.facets should probably be self.facets(). self.facets is an observable, not an array
var firstUnchecked = ko.utils.arrayFirst(self.facets, function (item) {
return item.IsChecked() == false;
});
return firstUnchecked == null;
},
write: function (value) {
// Below, self.facets should probably be self.facets(). self.facets is an observable, not an array
ko.utils.arrayForEach(self.facets, function (item) {
item.IsChecked(value);
});
}
});