I'm doing a bit of advanced work in KnockoutJS, whereby I generate some html outside of the KO process, apply bindings to them, and then insert them in my page.
The problem is housing the new html. My html is a couple of table rows, and when I do
var div = document.createElement('div');
div.innerHTML = template(viewModel);
the div strips out all the table content (my tr
and td
tags), presumably since divs can't contain table rows.
My cheesy workaround for the moment is below: use a tbody. But I'd like something a bit more generalized. I thought to use a document fragment, but that doesn't seem to have an innerHTML property to set.
What's the preferred way to handle this?
var div = document.createElement('tbody');
div.innerHTML = template(viewModel);
ko.applyBindingsToDescendants(bindingContext, div);
$(element).after($(div).contents());
As a workaround you could fetch the type of the parent node, create an empty detached new node of that type to house your contents, and later fetch the items from there.
It might look something like this, assuming you don't mind inserting the content before element
, rather than after it:
var container = document.createElement(element.parentNode.tagName),
frag = document.createDocumentFragment();
container.innerHTML = template(viewModel);
ko.applyBindingsToDescendants(bindingContext, container);
while (container.childNodes.length){
frag.appendChild(container.childNodes[0]);
}
element.parentNode.insertBefore(frag, element);
But it'd be better to figure out why your contents are stripped to begin with.
I think you will get this problem only with table parts.
Hence you can do this:
var templ = template(viewModel);
var newElement = document.createElement(
$(templ).is("tr, tbody, thead") ? 'table' : 'div'
);
newElement.innerHTML(templ);
Nit's answer works but its still a bit of a hack, I would use a custom template source instead.
First you need to create a engine that uses strings as source, like
var stringTemplateSource = function (template) {
this.template = template;
};
stringTemplateSource.prototype.text = function () {
return this.template;
};
var stringTemplateEngine = new ko.nativeTemplateEngine();
stringTemplateEngine.makeTemplateSource = function (template) {
return new stringTemplateSource(template);
};
Then you can use it from a custom binding like
ko.renderTemplate(template, bindingContext.createChildContext(data), { templateEngine: stringTemplateEngine }, element, "replaceChildren");
Were template
is a string containing the actual html