Jacob
Jacob

Reputation: 78840

Changing order that Knockout bindings are applied

I'm creating a Knockout binding that will work as a wrapper around a jQuery widget. This widget applies event handlers to child elements. Unfortunately, the widget's event handling is applied directly to the child elements, not delegated. The issue is that I have a foreach binding on the same element, but I need the custom binding to be applied after the foreach binding is applied.

Obviously, the right thing is to fix the jQuery plugin, but this isn't an option at this time. I'm wondering if there are any good workaround options for me. Is there way, for example, to do any of the following?

  1. Detect whether a particular binding has been applied
  2. Affect the ordering of binding application
  3. Safely force another binding to take place

Update:

One aspect I should mention is that this custom and foreach binding reside in a template. Therefore, solutions that directly modify the DOM won't work for me since it will actually modify the template.

Upvotes: 6

Views: 3055

Answers (3)

DrSammyD
DrSammyD

Reputation: 920

Add an after property on your bindingHandler with an array of dependencies

ko.bindingHandlers.myHandler = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        // Actual code
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        // Actual code
    },
    after:['foreach']
};

Upvotes: 7

Anders
Anders

Reputation: 17554

If your binding is dependant on foreach binding, why not call it from your custom binding? Then you don't even need to supply it in the data-bind attribute. I helped another SO user the other day, check how I call the options binding from within the custom binding

http://jsfiddle.net/w9bsc/42/

ko.applyBindingsToNode(element, { options: valueAccessor(), optionsCaption: caption, optionsText: optionsText  }, viewModel);

Upvotes: 3

Jacob
Jacob

Reputation: 78840

I found a workaround, but it's more of a hack than I like. I'll still wait for better answers.

What I did was simply this:

ko.bindingHandlers.myHandler = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        if (allBindingsAccessor().foreach) {
            setTimeout(doInit, 1);
        } else {
            doInit();
        }

        function doInit() {
            bindingContext.initializedMyHandler = true;
            // Actual code
        }
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        if (bindingContext.initializedMyHandler) {
            doUpdate();
        } else {
            setTimeout(doUpdate, 1);
        }
        function doUpdate() {
            // Actual code
        }
    }
};

Basically I just defer execution using a timeout. That way, the rest of the binding handlers will execute first.

Upvotes: 2

Related Questions