proper cleanup of template and models

854 views
Skip to first unread message

mcoolin

unread,
Aug 8, 2011, 6:45:04 AM8/8/11
to knock...@googlegroups.com
Hi All,

I have an implementation that used multiple viewModels, the site is dynamic, for me this means I build pages with 4-20+ different viewmodels.

Between folder I have been using jQuery to remove my content areas. Models I reset with model = [];

Should I be calling something in knockout to break the bindings before remove?


rpn

unread,
Aug 8, 2011, 7:41:29 AM8/8/11
to knock...@googlegroups.com
You can call ko.removeNode on an element to clean up it and its descendants and then remove it.  You can call ko.cleanNode to just do the clean up without removing it.


Scott Messinger

unread,
Aug 8, 2011, 11:27:03 AM8/8/11
to knock...@googlegroups.com
What does ko.cleanNode do? Is the node in question a DOM node or part of a viewmodel?

-Scott

rpn

unread,
Aug 8, 2011, 11:51:49 AM8/8/11
to knock...@googlegroups.com
The node is a DOM node.  This is essentially what the template binding is doing for you when it re-renders content.  It would only be necessary to call this, if you are manually wanting to "unbind" some elements.

It removes any data that KO stored with the element, removes any jQuery data (if jQuery is present), and calls any disposal callbacks registered against the element.

There was a time that KO relied on the fact that a dependentObservable from a binding would eventually be re-evaluated and at that time if the element was no longer in the DOM, it would dispose itself.  However, there was a chance for memory leaks when an observable was never updated, but templates kept creating new bindings against it.  Now, there are proactive disposal callbacks registered for elements by the bindings, so if an element is removed by KO APIs then it will trigger the disposal.

This also allows you to do something like this in a custom binding now:

      //handle disposal (if KO removes by the template binding)
     
ko.utils.domNodeDisposal.addDisposeCallback(element, function(){
          
$(element).datepicker("destroy");
      });

    

For the original question, one way to avoid having to do this yourself is to have a "master" view model that manages child view models in observables with the template binding rendering each child, if it is populated.

Scott Messinger

unread,
Aug 8, 2011, 12:44:28 PM8/8/11
to knock...@googlegroups.com
For the original question, one way to avoid having to do this yourself is to have a "master" view model that manages child view models in observables with the template binding rendering each child, if it is populated.

Would you
1) call ko.applybindings(masterViewModel.childViewModel) for each childViewModel? \
2) call ko.applyBindings(masterViewModel)
3) call ko.applyBindings(masterViewModel.childViewModel, DOMnode) and apply the childViewModels to specific dom nodes?

Thanks for your help, Ryan!

rpn

unread,
Aug 8, 2011, 1:05:06 PM8/8/11
to knock...@googlegroups.com
There are different ways that you could do it.  

Here are my opinions the options:

For #1, you would likely not want to do this unless you are using: https://github.com/hunterloftis/knockout.namespaces.  Otherwise, you will potentially get multiiple event handlers bound to some elements or you will error out when one view model does not have the structure that a binding expects from another.


For #2, this is the way that I think works best.  The basic idea would be something like:

var viewModel {
    headerModel ko.observable(...),
   contentModel ko.observable(...),
   sidebarModel ko.observable(...),
   footerModel ko.observable(...
};


Then, bind your sections using the template binding like:

<div data-bind="template: { name: 'sidebarTmpl', data: sidebarModel }"></div>

The nice part is that if sidebarModel is null, then it just won't render anything. Also, when inside of the template, then you are able to reference your properties directly (for example, title instead of sidebarModel.title).  The other part that can be helpful is that you are still able to reference items between the "child" view models either by passing them in through templateOptions or directly if they have global scope.


For #3, if you want to maintain view models in different variables/structures (they do not have the same parent), then this is a good way to do it.  You are limited though by the fact that the elements that you bind against can't be ancestors of each other or you can get duplicate bindings.  If you did want to share data between view models, you could still accomplish this by adding properties are references between them.


So, I think that any of those methods can work, but #1 would need to be coupled with the namespaces plugin.  Hope this helps.   

mcoolin

unread,
Aug 9, 2011, 8:08:05 AM8/9/11
to knock...@googlegroups.com
In my case I have a master view model which contains an array of view models, my master model does not get rendered I'm just using the dependent observables to handle application events. My controller jcontroller in this case takes care of laying down the templates, applying the bindings for each and cleaning up.

So far everything is working very nicely. 

My biggest problem so far was trying to use the mapping plugin, I'm gaining a better understanding of what it does, but so far prefer sticking with native knockout.

mcoolin

unread,
Aug 9, 2011, 8:11:10 AM8/9/11
to knock...@googlegroups.com
Here is the model I'm working with:

    appModel = {
        folderID: ko.observable(null),
        language: ko.observable("E"),
        // app/folder security goes here
        isAdmin: ko.observable(false),
        isSuperAdmin: ko.observable(false),
        hasDisplayLogic: ko.observable(false),
        isMockingOn: ko.observable(true),
        showHelp: ko.observable(true),
        controller: null,
        hashhist: [],

        appHeaderModel: null,
        appMenuModel: null,
        appFolderBarModel: null,
        appControlModels: [],
        appTemplates: new Object(),
        appLogicModel: {    // mapped to wd_Logic using template ?
            logic: ko.observableArray(),
            showLogic: ko.observable(false)
        },     
        appFooterModel: null
};

Reply all
Reply to author
Forward
0 new messages