Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my KnockoutJS custom binding being triggered?

I've got an odd situation. Basically, I have two custom bindings which are used to animate the DOM element to it's new value. These are aWidth and aRight, which animate the width and right values respectively.

I've implemented the bindings like this:

<div class='classname' data-bind="aRight: right, aWidth: containerWidth, style: { zIndex: zindex, left: (left() + 'px'), height: (containerHeight() + 'px') }">

... and the custom bindings look like this:

            ko.bindingHandlers.aWidth =
            {
                update: function (element, valueAccessor, allBindingsAccessor, context)
                {
                    // Get the value accessor
                    var value = valueAccessor();

                    // Get the new width and the duration of the animation
                    var newWidth = ko.utils.unwrapObservable(value);
                    var duration = 500;

                    $(element).animate({ width: newWidth }, duration, "swing");
                }
            };

            ko.bindingHandlers.aRight =
            {
                update: function (element, valueAccessor, allBindingsAccessor, context)
                {
                    // Get the value accessor
                    var value = valueAccessor();

                    // Get the new width and the duration of the animation
                    var newRight = ko.utils.unwrapObservable(value);
                    var duration = 500;

                    $(element).animate({ right: newRight }, duration, "swing");

                    console.log("aRight Called: newRight - " + newRight + ", duration - " + duration);
                }
            };

So the problem comes around when we change an observable other than my two custom bound observables, for instance zindex.

If we change the observable zindex, the value gets updated correctly in the DOM, but for some reason, my aRight binding also gets triggered!...

I don't have any reference to it in my aRight custom binding so there surely can't be a dependency?

My aRight binding also gets triggered when my aWidth binding is triggered too, which is also a little odd!

Does anyone have any ideas about this?

Thanks a lot!

Andy.

Update

This is the part of the view model which updates the index, which when causes my aRight custom binding to fire (this is very much psudo-code by the way!):

    var page = function()
    {
        this.zindex = ko.observable(0);
        this.right = ko.observable(0);
        // and other observables....
    }

    var viewModel = function()
    {
        var pages = ko.oberservableArray();
        // populate the pages array etc...

        this.someMethod = function()
        {
            // Do some stuff...
            this.anotherMethod();
            // Do some other stuff
        }
        .bind(this);

        this.anotherMethod() = function
        {
            var pageCount = this.pages().length;
            for (var pageNum = 0; pageNum < pageCount; pageNum++)
            {
                var page = this.pages()[pageNum];
                page.zindex(/* a different value */); // This is what causes my aRight binding to fire...
            }
        }
        .bind(this);
    }

Update

I just read a post here: http://groups.google.com/group/knockoutjs/browse_thread/thread/26a3157ae68c7aa5/44c96d1b748f63bb?lnk=gst&q=custom+binding+firing#44c96d1b748f63bb

Saying:

Additionally, a binding will have its update function run again, if another binding in the same data-bind attribute is triggered as well.

Does this mean that what I am seeing is that my custom binding is being triggered when any other binding the in data-bind attribute is triggered (it just so happens maybe that zindex is the first that I see change)? Is this not a bit odd/wrong?...

Update

I've got a simple fiddle which I think pretty much sums up my problem. It seems like any binding on the same data-bind attribute as much custom binding will cause it to update!

http://jsfiddle.net/J6EPx/2/

Hmmm... guess I'll have to work around it by manually checking in my custom binding as to whether the value has actually changed or not!! Does this not defeat that actual point of a binding???

I've also posted a more precise question on the Knockout forums: http://groups.google.com/group/knockoutjs/browse_thread/thread/d2290d96e33f1d5a

like image 481
Andy Avatar asked Nov 30 '11 10:11

Andy


1 Answers

This is currently by design. All bindings in a data-bind are triggered when any of bindings fire. This is because they are all wrapped in a single dependentObservable. In some cases, bindings have a dependency between each other (if options are updated, then value needs to run to ensure that it is still a valid value). However, in some cases, this does cause a problem.

There is a different pattern that you can use when creating a custom binding that helps to mitigate this behavior. Instead of defining the guts of your functionality in the "update" function, you would actually create your own dependentObservable in the "init" function. It would look like:

ko.bindingHandlers.custBinding= {
    init: function(element, valueAccessor) {
        ko.dependentObservable({
            read: function() {
                ko.utils.unwrapObservable(valueAccessor());
                alert("custBinding triggered"); 
            },
            disposeWhenNodeIsRemoved: element
        });
    }
};

http://jsfiddle.net/rniemeyer/uKUfy/

like image 51
RP Niemeyer Avatar answered Nov 12 '22 15:11

RP Niemeyer