Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple view models per page and loading templates asynchronously

I am developing an application using - 1. Knockout.js 2. Knockout.js External Template Engine (ifandelse) [indirectly using infuser, trafficcop], 3, Sammy JS, Require JS, etc.

To promote modularity and ease of development, I am architecting application to use multiple viewmodels per page. Using binding tips from Ryan Niemeyer (http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html), which describes how to bind multiple viewmodels using overloaded version of applyBindings. This technique worked fine until I decided to use Knockout.js External Template Engine library from Jim Cowart, which help me separate out templates into files, and load them asynchronously. Reason it does not work is simple, for applyBinding to bind viewmodel to a specific DOM element, DOM element should be present (that is not the case as template is being requested and loaded asynchronously by KO External Template Engine). I can't use afterRender, etc either.

Has anyone come across this scenario? Any suggestion, direction in this regards will be very helpful. Am I missing some feature of KO that I can use?

For now, as a work around, I have added 'templateLoaded' callback while defining template in HTML:

<!--ko template: {name: 'header', templateUrl: '/templates/shell', templateLoaded: function () { header.bindViewModel.call(header) }} -->
<!--/ko-->

HTML Template:

<!-- ko stopBinding: true -->
<header id="divHeader">
    <!-- Template Code using Header viewmodel -->
</header>
<!-- /ko -->

Header viewmodel:

bindViewModel = function () {
    ko.applyBindings(this, $("#divHeader").get(0));
}

and modified Knockout.js method "executeTemplate()" with following:

if (haveAddedNodesToParent) {
    if (options.templateLoaded ) {
        options.templateLoaded.call(bindingContext['$data']);
    }

    activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
    if (options['afterRender'])
        ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
}

Now, callback is called after template is retrieved asynchronously, parsed and loaded into DOM. This help bind viewmodel to correct element.

like image 428
Jyotindra Avatar asked Nov 13 '22 08:11

Jyotindra


1 Answers

I'm using the same architecture as you on MVC4 in my work, the answer is easy Jyotindra use applybindingsTonode and when you complete the request to the server for example

  function loadMenu(){
       require('menu.js', function(menuViewmodel){
            ko.applyBindingsToNode(containerElement, { template: { name: 'itemTemplate', foreach: items }, menuViewmodel);
       }
    }

Regards! PS: you can look also this knockout.js - lazy loading of templates

like image 116
David Torroija Avatar answered Jan 04 '23 01:01

David Torroija