24

I have a knockout binding handler that uses plupload for drag and drop and ajax uploads.

To use the plupload script I create an instance of plupload which in turn is binding event listeners to DOM elements.

That works fine.

However, I have a list of "folders" and when I click a folder I display a list of files in that folder. I reuse the same DOM elements for this by binding selectedFolder().documents using foreach.

The problem I have is that in my binding handler I do all my plupload stuff in the init function and since I reuse the DOM elements they get multiple event handlers bound to them. This causes the drag and drop events to be sent to alla handlers. This means that if I drop a file on the rendered file list, the drop event fires on all previously rendered file lists too.

What I am looking for is some sort of teardown or cleanup function in the binding handler, so that I can unregister all of the events whenever a file list get unrendered (is that a word?).

Maybe we cannot detect unrendering? How would I then handle this? I would prefer not to have a global instance, since that would prevent me from using the binding on multiple places at the same time.

Sorry about not giving you any code. I'm on my cell phone atm.

Cheers!

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
Mikael Östberg
  • 16,982
  • 6
  • 61
  • 79

2 Answers2

32

You can register a handler that will be executed whenever KO removes elements (like when a template is re-rendered). It looks like:

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

So, in your "init" function you would register a dispose callback for the element that is being bound and you would have an opportunity to run whatever clean-up code that you would like.

RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • That would definitely solve my problem. Pity it wasn't called unrender though. Thanks RP! – Mikael Östberg Apr 25 '12 at 22:38
  • If you're mixing in with other libraries that do their own DOM manipulation (common in single page applications) then you might want to consider the jQuery approach below which triggers for any DOM removal (not just the ones triggered by knockout). Useful if you already reference jQuery. – Sebastien Martin Jun 12 '13 at 16:03
3

I believe the solution provided here will only work if Knockout is the one that removes the DOM node (ie when it rejigs templates). I had a hard time getting it to trigger under certain conditions. There might be scenarios where you need a callback to be executed regardless of how your element got removed; whether it be with Knockout, or via jQuery.html(), etc (especially in a single page application).

I brewed a different approach for adding such a hook with a little help from jQuery. Using the special events API (which are well described here), you can add a method that gets execute when a particular event is removed from a DOM node (something that happens on teardown).

If you are using Knockout in conjunction with jQuery, you can wrap this into a knockout binding to look something like this:

ko.bindingHandlers.unload = {
    init: function (element, valueAccessor) {
        var eventName = 'your_unique_unLoad_event'; // Make sure this name does not collide
        if (!$.event.special[eventName]) {
            $.event.special[eventName] = {
                remove: function (o) {
                    o.data.onUnload()
                }
            };
        }
        $(element).on(eventName, { onUnload: valueAccessor()}, $.noop);
    }
};

You can then use this on any element like this:

<div id="withViewModelMethod" data-bind="unload: aMethodOnMyViewModel" />
<div id="withInLineMethod" data-bind="unload: function() { /* ... */ }" />

I owe credits to this SO post.

Community
  • 1
  • 1
Sebastien Martin
  • 1,341
  • 11
  • 25
  • I am not sure how you got this **var eventName = '_unique_unLoadEventName____12345';** Just trying to understand because I have ran into similar situation. – nimgrg May 23 '13 at 09:07
  • That's just a string that I pulled out of a hat. It can be anything you want, I just wanted to make sure it was unique and didn't collide with any other plugins or 3rd party code that would add their own event handlers. It's slightly exaggerated in this example.. – Sebastien Martin Jun 12 '13 at 15:57
  • maybe just change '_unique_unLoadEventName____12345' to 'your_unique_unLoad_event' – Jason J. Nathan Jul 14 '13 at 09:17