franck102
franck102

Reputation: 341

How do you use native Knockout templates with the KO external template engine

I am using the KO external template engine to break down my single page webapp into multiple files, but the templates I am loading only contain KO markup.

Despite researching the topic extensively I can't figure out how to reliably apply KO bindings to the templates that are being loaded.

The key points are that:

This is is the code I have, which has at least two issues:

Any suggestion for a clean, reliable method of applying bindings once and only once to external template nodes as they are being added to the DOM?

Upvotes: 1

Views: 1122

Answers (4)

franck102
franck102

Reputation: 341

I came up with something that works using an afterRender callback... although a bit messy IMHO, improvements welcome.

The afterRender callback is invoked twice for some reason, the second time around with an empty object, hence the test for hasOwnProperty('nodeType').

isBound() checks if the bindings have already been applied to the element - an attempt to add a custom marker CSS ('ko-applied') class to the element after the applying the bindings didn't work reliably.

Not sure that copying the koElements array is actually needed, but without that I have ended up with undefined elements[i] in the loop, so the template loader may be asynchronously updating the array while afterRender is running.

<div id="templateDiv" data-bind="template: { name: currentView(), afterRender: applyTemplateBindings }"></div>

self.applyTemplateBindings = function(koElements) {
            var elements = koElements.slice();
            for (var i = 0, len = elements.length; i < len; i++) {
                var element = elements[i];
                if (element.hasOwnProperty('nodeType') && ! $(element).hasClass('infuser-loading') &&
                    ! isBound(element)) {
                    ko.applyBindings(self, element);
                }
            }
        };

        var isBound = function(node) {
            return !!ko.dataFor(node);
        };

Upvotes: 0

Anders
Anders

Reputation: 17554

I have made a Template engine for KO

https://github.com/AndersMalmgren/Knockout.Bootstrap.TemplateStore/wiki

It requires a Owin enabled web server, once configured it understands that a ViewModel named FooViewModel should be connected to the View called FooView

Install using nuget (For ASP.NET)

Install-Package Knockout.Bootstrap.TemplateStore.SystemWeb

It is also designed to be easy to use in a SPA

Demo https://github.com/AndersMalmgren/Knockout.Bootstrap.Demo

Upvotes: 0

G&#244;T&#244;
G&#244;T&#244;

Reputation: 8053

This is not a solution on how to use the templates, but this is how I split into files using the with binding.

Suppose you have a page like this:

<div data-bind="with: block1">
    <input data-bind="value: yourFirstInput" />
    <!-- more markeup -->
</div>
<div data-bind="with: block2">
    <select data-bind="options: dropDownlist2options"></select>
    <!-- more markeup -->
</div>

You can put the block1 in a file (I use ascx), block2 in another file.
Then in your viewmodel you have something like:

var viewmodel = function () {
    var self = this;
    this.block1 = ko.observable();
    this.block2 = ko.observable();
}
var vm = new viewmodel();
ko.applyBindings(vm);

This way you applyBindings on the whole page. The with binding will handle the check for null/undefined objects and display the blocks only when the obects are instanciated.

You can then do something like this when you want to display block1:

vm.block1({ yourFirstInput: ko.observable('aa')});

Actually I do that with the mapping plugin, this is just an example.

Live demo (notice how the block1 appears after the setTimeout)

Upvotes: 0

rjdmello
rjdmello

Reputation: 863

if i got you point then you can do this

load you template in one base file in js using get and assign to script tag as below

var script   = document.createElement("script");
               script.id  = "YourTemplateName";
               script.type  = "text/html";
               script.text  = result.Value; //template data
               document.body.appendChild(script);

and in your index/base html file assign template

<div id="OtherTemplateDiv" data-bind="template: { name: 'YourTemplateName' }">
            </div>

Upvotes: 1

Related Questions