//simulate data that would be retrieved from server
var fakeDataToRetrieveFromTheServer = {
view: "<li data-bind=\"text: name\"></li>",
edit: "<li><input data-bind=\"value: name\" /></li>"
};
//------------------------------------------------------------------------------
//a templateSource that retrieves data via AJAX and provides loading/error templates
ko.ExternalTemplate = function(key, loadingTmpl, errorTmpl) {
this.key = key;
this.loaded = false;
this.inProgress = false;
//templates to use while loading and when an error happens. fall back to a default.
this.loadingTmpl = loadingTmpl || this.loadingTmpl;
this.errorTmpl = errorTmpl || this.errorTmpl;
//the current template
this.currentTmpl = ko.observable(this.loadingTmpl);
this.currentTmpl.data = {}; //a place to tuck away meta-data about the template
};
ko.utils.extend(ko.ExternalTemplate.prototype, {
//read/write meta-data about the template (has it been rewritten already; not used for native templates currently)
data: function(key, value) {
if (arguments.length === 1) {
return this.currentTmpl.data[key];
}
this.currentTmpl.data[key] = value;
},
//read/write the actual template text
text: function(value) {
if (!this.loaded) {
this.getTemplate();
}
if (arguments.length === 0) {
return this.currentTmpl();
} else {
this.currentTmpl(arguments[0]);
}
},
//retrieve our actual template via AJAX
getTemplate: function() {
if (!this.inProgress) {
this.inProgress = true;
$.ajax({
type: 'POST',
url: '/echo/json/',
data: {
json: ko.toJSON({ key: this.key }),
delay: 1
},
context: this,
success: function(data) {
this.currentTmpl(fakeDataToRetrieveFromTheServer[this.key]);
},
error: function(data) {
this.currentTmpl(this.errorTmpl);
},
dataType: 'json'
});
}
},
//some defaults to use for the loading/error templates
loadingTmpl: "<div><img src='http://rniemeyer.github.com/KnockMeOut/Images/loading.gif' /><div>" ,
errorTmpl: "error!<br/>"
});
//------------------------------------------------------------------------------
//define a new template engine that knows how to work with our new templateSource
function myExternalTemplateEngine(templates) {
var result = new ko.nativeTemplateEngine();
result.templates = templates;
result.makeTemplateSource = function(templateName) {
return this.templates[templateName];
}
return result;
}
//------------------------------------------------------------------------------
//actual page code
function Item(id, name) {
return {
id: ko.observable(id),
name: ko.observable(name)
}
}
var viewModel = {
templates: {
viewTemplate: new ko.ExternalTemplate("view"),
editTemplate: new ko.ExternalTemplate("edit", "loading...<br/>", "error2")
},
isEditable: ko.observable(false),
items: ko.observableArray([
new Item(1, "one"),
new Item(2, "two"),
new Item(3, "three")
]),
addItem: function() {
this.items.push(new Item(0, "new"));
},
whichTemplateToUse: function() {
return viewModel.isEditable() ? 'editTemplate' : 'viewTemplate';
}
};
//make this new template engine our default engine
ko.setTemplateEngine(new myExternalTemplateEngine(viewModel.templates));
ko.applyBindings(viewModel);
Editable: <input type="checkbox" data-bind="checked: isEditable" />
<h2>string template</h2>
<ul data-bind="template: { name: whichTemplateToUse, foreach: items }"></ul>
<button data-bind="click: addItem">Add</button>
h2 { font-size: 1.1em; font-weight: bold; }