I am using SPServices and KnockoutJS to load some data on a page, specifically a list of projects and the number of tasks for each project.
I have simplified the example a bit but what I am having issues with is to get the value of the ajax call async. I solved it cheaply by using jQuery to bind the result instead of returning anything:
function ProjectModel(title, id) {
var self = this;
self.id = id;
self.title = title;
self.tasks = ko.computed(function() {
$().SPServices({
operation: "GetListItems",
async: true,
webURL: "/projects/" + self.id,
listName: "Tasks",
CAMLQuery: // query..,
CAMLViewFields: "<ViewFields><FieldRef Name='ID' /></ViewFields>",
completefunc: function (xData, Status) {
$(".tasks-" + id).text($(xData.responseXML).SPFilterNode("z:row").length);
}
});
});
}
I had a look at these sites Async dependantobservables async computed observables
Is this the way to go? Edit:
OK so some more code (have stripped out some stuff here):
function onGetProjectsCompleted(projects) {
var projectViewModel = new ProjectViewModel();
projectViewModel.init(projects);
ko.applyBindings(projectViewModel);
}
function beforeLoadingProjects() {
$(".loadingMessage").show();
}
function initProjectsView() {
ProjectsRepository.getOpenProjects(beforeLoadingProjects, onGetProjectsCompleted);
}
function ProjectViewModel() {
var self = this;
self.openProjects = ko.observableArray();
self.init = function initProjectViewModel(projects) {
$.each(projects, function() {
self.openProjects.push(this);
});
});
};
}
var ProjectsRepository = {
getOpenProjects: function (beforeComplete, onComplete) {
var options = {
operation: "GetListItems",
completefunc: function(xData, status) {
var projects = new Array();
$(xData.responseXML).SPFilterNode("z:row").each(function() {
var item = $(this);
projects.push(new ProjectModel(
item.attr("ows_Title"),
item.attr("ows_ProjectID")
));
});
onComplete(projects);
}
};
beforeComplete();
$().SPServices(options);
}
};
Please note, I don't want to list the tasks for each project, I just want to have a property with the Number of tasks for each project, i.e. no array.
Thanks again.
I can't tell how you are trying to use the class (Model) above, but @anders is correct: your model should not manipulate DOM element directly... That's what Knockout's view binding is for...
Try this (disclosure: I did not test this in a live environment):
function ProjectModel(title, id) {
var self = this;
self.id = id;
self.title = title;
self.tasks = ko.observable(0);
$().SPServices({
operation: "GetListItems",
async: true,
webURL: "/projects/" + self.id,
listName: "Tasks",
CAMLQuery: // query..,
CAMLViewFields: "<ViewFields><FieldRef Name='ID' /></ViewFields>",
completefunc: function (xData, Status) {
if (Status !== "success") {
alert("failed to get tasks! Ajax call error.");
return;
}
self.tasks(
$(xData.responseXML).SPFilterNode("rs:data").attr('ItemCount')
);
}
});
}
<div id="mytemplate">
<h2>
<span data-bind="text: title"></span> Project has
<span data-bind="text: tasks"></span> Tasks
</h2>
</div>
ko.applyBindings(
new ProjectModel("your project title", "your project ID"),
document.getElementById("mytemplate")
);
Try to use the above and see if you get output... The number of tasks will be initially zero, but if your tasks list is valid and tasks are there, then it will update itself once the query completes.