rniemeyer / knockout-classBindingProvider

An alternate binding provider for Knockout that allows you to define your bindings in a JavaScript object and key into it from the markup.
MIT License
101 stars 27 forks source link

Multipe return paths from bindings #23

Open egucciar opened 8 years ago

egucciar commented 8 years ago

Hi,

In our code we use bindingClassProvider extensively and in some scenerios we choose to dynamically return based upon the viewModel

example:

binding: function () {
    if (this.hover()) {
        return {
            event: {
                mouseover: this.hoverAction
            }
        };
    } else {
        return { };
    }
}

Will this work as expected and essentially "remove" the hoverAction handler when hover is false? Lets say hover can change dynamically from true to false. We would expect then for the event binding to have been disposed since the new return does not have it, but we are seeing that the event binding is persistant but its valueAccessor() now does not exist and it throws an error.

Thanks for your help.

mbest commented 8 years ago

Even though you can update the event binding's value, the actual set of events cannot be changed, only the handlers. Thus you could change your code to this:

binding: function () {
    return {
        event: {
            mouseover: this.hover() ? this.hoverAction : function () { }
        }
    };
}
egucciar commented 8 years ago

Michael,

Thank you so much for answering and confirming my observation. Can you explain how it works when the binding gets re-evaluated? My understanding is that the function for the binding is a computed so whenever anything unwrapped gets updated, the function will be re-evaluated.

But then what happens for the bindings that get returned? Are they re-initialed? updated? Please explain as this is confusing for me and my colleagues.

Thanks Erica

mbest commented 8 years ago

The event binding handler establishes the event bindings only once. This is just to set up listeners for the events. When an event happens, it checks the binding value for the handler function for that event.

egucciar commented 8 years ago

So do all bindings act like the "event" binding or does the "event" binding get special handling? What is the underlying mechanism which occurs when a binding gets re-evaluated from the class provider? I used the event binding as an example, but am looking for an explanation in regards to how any binding works so I can predict what will happen in my application better.

Thank you.

mbest commented 8 years ago

The API is designed so that the object given to event is really a part of the binding itself. In my Punches library, I've made it possible to use event.[eventname] as a binding. Thus you could bind instead like this:

binding: function () {
    return {
        'event.mouseover': this.hover() ? this.hoverAction : function () { }
    };
}

This format, I think, makes it much clearer that the binding is to the mouseover event and not to a generic event handler. The other bindings that work this way are css, style, and attr.

egucciar commented 8 years ago

So do all of those bindings act that way because they expect objects to be passed to the valueAccessor? How about a custom binding?

Let's say I have a custom binding that I pass an object to, for instance your very own jqAuto which we have been using. We tried to pass a property "disabled" through to the options but found that when disabled changed, the jqAutocomplete didn't update the control with the new option. We ended up using knockout disable binding , but it's curious how anything gets updated with the bindingClassProvider.

mbest commented 8 years ago

I'm not @rniemeyer, but I'm one of the Knockout developers, and I thought I could answer your question. A good way to learn how these pieces work together is to load up a sample in Chrome and put some breakpoints in your binding function and custom binding handler code. Look at the stack trace or step through and see how and when those functions are called.

egucciar commented 8 years ago

Okay, yeah. I've done this with a lot of different knockout features but I thought I had it figured out for bindings. I basically thought, okay, so the binding is probably completely re-instatiated, because I always saw my "init" functions being called after a binding got re-evaluated. And you know since you cant apply bindings to the same node I also assumed "cleanNode" got called to dispose of the previous bindings..every time. I was wrong, of course, as this example with Event showed me.

I apologize for not realizing you and rniemeyer werent one on the same, I remember your name from knockout so I kinda mixed it up.

I am going to set up a test tomorrow with a few breakpoints and check what happens in various scenerios. I really want to know how it works because it certainly affects how I code my app! Thanks for all your help!

mbest commented 8 years ago

The only time you would see init called more than once for the "same" binding is if the node has actually been re-rendered, like within the template or with bindings.

rniemeyer commented 8 years ago

@egucciar - happy to try to help, if you have any specific scenarios or combinations that you are causing you trouble.

@mbest - thanks for the responding on this thread

egucciar commented 8 years ago

okay, i understand better now. Im not sure if I can reproduce a scenerio like the one I described. We have built very large apps with lots of bindings and knockout, so perhaps previous assumptions i had were wrong. We are writing complex forms and UIs and lots of our inputs have multiple bindings on them, controlling lots of things. The handful of times I made the assumption that bindings being re-computed meant the bindings got udated some how, it has helped me solve bugs when one thing updates and another thing changes unexpectedly on the same element. By seperating my bindings out into smaller bindings, (mix n match binding classes), I've resolved bugs and things update in isolation.

I did just a quick test with one of my datepicker bindings where i brought other observables back in and couldnt "reproduce" the bug I had. The init binding doesn't get called.

So I still dont understand how I could resolve a bug by separating out bindings, but since I can't reproduce it now, I dont expect an exact answer. Michael you have provided a lot of good insight and I definitely need to dig deeper into BindingClassProvider.

Thanks

mbest commented 8 years ago

@egucciar I think you're running into the problem that all bindings fire together. This was fixed in Knockout 3.0, but the classBindingProvider still works like 2.x. Thus if you have multiple bindings together, when one is updated, the others will update as well.