Connecting knockoutjs to an already populated DOM element
I'm working on a page which is being server from an ASP.NET MVC3 app that I want to use KnockoutJS in.
On the View I'm getting passed a populated Model (which I'm converting to a KnockoutJS viewModel) and I want to build up some of the DOM server-side. This seems开发者_开发百科 logical since I already have the objects there to generate the initial HTML and it'll provide a better experience to the users as there wont be a delay between the page load and the DOM population (it also means I have basic functionality for non-JavaScript clients).
After doing some research I'm assuming I need to create a custom bindingHandler
so I created this:
ko.bindingHandlers.serverForEach = {
init: function() { /* no-op */ },
update: function() {
//call off to the built in loop handler
}
};
So my thinking was that I create a handler which does nothing in the init
phase (as the DOM is already populated) and in the update
phase I'll just insert the new value. I'd like to leverage the built-in templating so this can be a generic solution as well.
The problem is that everything falls apart in this scenario. If the init
does nothing then the update
fails as the bindingContext that comes in as the last argument seems wrong, and if I include the init it'll destroy the existing HTML.
Anyone tried this/ know what I should do to support this scenario or is it just too far removed from what you can/ should do?
Also, I don't want to have a separate DOM element for the server generated HTML than the Knockout HTML.
I think I understand what your problem is. You are wanting to populate the markup initially for non-js users and simply overwrite it when binding to KO.
For simple bindings like text this is not an issue simply include the data-bind attribute in the markup from the server or include at runtime and away you go. For the template foreach binding things are a little more difficult since it will append values when bound.
Here is a jsfiddle that describes the above scenario and demonstrates the solution below. It's a little basic but should get you started.
The basic idea is to clear the element on init and then delegate the rest to the existing template binding.
ko.bindingHandlers.serverForEach = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
// clear the list first
$(element).children().remove();
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
ko.bindingHandlers['template'].update(element, valueAccessor, allBindingsAccessor, viewModel);
}
};
Hope this helps.
In case anyone needs this to work for knockout.js 2.0+ (note the bindingContext
in update)
ko.bindingHandlers.serverForEach = {
init: function(element, valueAccessor) {
$(element).children().remove();
return ko.bindingHandlers.template.init(element, valueAccessor);
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
return ko.bindingHandlers.template.update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
}
};
精彩评论