//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; }