James
James

Reputation: 7897

Custom Knockout binding failing when invoking default knockout binding

Overview

When creating a knockout custom binding that contains a call to a knockout default binding, my custom binding stops working after the first call.

To see the issue, in the JSFiddle example, change the selected item in the ddl once. Text changes as expected. Change it again, and nothing happens. Fail.

Details

I am seeing some odd behavior when extending the default knockout binding "html" with my own custom binding "htmlFade". The behavior I am looking for is the same as the html binding, but with a JQuery fade animation to fade DOM elements in and out.

Code samples below. A full JSFiddle example can be see here.

The HTML looks like this:

<select data-bind="options: names, value: selectedName"></select>  
<data-bind="htmlFade : selectedName" class="main"></div>

The Json Data looks like this:

var viewModel = {
    names: ko.observableArray(['Bob', 'Jon']),
    selectedName: ko.observable('Bob')
};

The Custom Binding looks like this:

ko.bindingHandlers.htmlFade = {
    init: function(element, valueAccessor) {
        ko.bindingHandlers.html.init();
        $(element).hide();
    },
    update: function(element, valueAccessor) {
        $(element).fadeOut(700, function() {
           ko.bindingHandlers.html.update(element, valueAccessor);           
           $(element).fadeIn(700);
        });        
    }
};

I am intentionally deferring to the default html handler with the line "ko.bindingHandlers.html.update(element, valueAccessor)", because my goal is to extend the behavior, not recreate.

The problem I am having is that the above code works the first time select list is changed. After that it fails.

I created another JSFiddle example where instead of extending the behavior, I recreated it by adding the following line "http://jsfiddle.net/jamshall/kYwEE/1/" (copied from the knockout source for the default html binding) in place of the above referenced html.update call. This seems to work fine.

My question, then, is why does my Custom Binding stop working after the first call when I include a call to a default binding from withing it? Or, to make it simple, why does JSFiddle1 not work but JSFiddle2 work?

Thanks for any help

Upvotes: 4

Views: 657

Answers (1)

RP Niemeyer
RP Niemeyer

Reputation: 114792

You can think of the update function of a custom binding like a computed observable (Knockout does use computed observables to track dependencies when the bindings on an element are executed). So, in your custom binding you are not grabbing a dependency to the observable that you are bound against, because your code is being executed asynchronously.

In your update, you would probably want to do something like:

update: function(element, valueAccessor) {
    //just grab dependency
    ko.utils.unwrapObservable(valueAccessor());

    $(element).fadeOut(700, function() {
       ko.bindingHandlers.html.update(element, valueAccessor);           
       $(element).fadeIn(700);
    });        
}

So, we just access the value of the observable to grab a dependency. ko.utils.unwrapObservable just safely handles retrieving the value no matter if it is a non-observable or observable.

Updated sample: http://jsfiddle.net/rniemeyer/6UtsP/10/

Upvotes: 1

Related Questions