Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

two-way binding on variable with setter causes initial call to setter #258

Closed
mbroadst opened this issue Dec 10, 2015 · 15 comments
Closed

Comments

@mbroadst
Copy link
Contributor

I have a pretty involved grid component that uses two-way binding to (among other things) control pagination and sort order data. For sort order, I have a number of grid-column-header elements which two-way bind to an order variable, and toggle it based on click of that header: works fantastically. What I am not noticing (and hadn't in previous versions) is that on the initial display, each of the individual column headers is causing a set on the order variable which is of course leading my code to update the entire table N times (N === number of columns). Is there any way to avoid this behavior?

relevant example: http://plnkr.co/edit/3tsFCI0xG30QHYU6rsPt
note that when you start the example you have 20 console.log statements for sharedValue set

@mbroadst
Copy link
Contributor Author

Hmm, this might be more of an issue with the design on my part. It seems that even if I short circuit the initial set call back to the origin, every time I change it in each grid-column-header that also triggers N-col sets. So it seems the behavior is inherent to the binding type?

@CasiOo
Copy link
Contributor

CasiOo commented Dec 10, 2015

Changing sharedValue directly from the App class, will call sharedValue's set method twice.
I wouldn't expect it to be set twice either. The bindeable is clearly setting the value again, after it registered the change.

I don't believe it should behave that way, but I have been wrong before haha

@mbroadst
Copy link
Contributor Author

@CasiOo yeah I'm hoping this is indeed a bug, it would save me some refactoring time 😄

@mbroadst
Copy link
Contributor Author

I've updated plunker with a button on the top to change the sharedValue directly from the view model, so you can see that setting it there also causes 20 additional sets to the variable

@EisenbergEffect
Copy link
Contributor

@jdanyow Can you look into this or comment?

@jdanyow
Copy link
Contributor

jdanyow commented Dec 11, 2015

The controller calls the BehaviorPropertyObserver. Maybe this isn't necessary? Then what happens is the behavior property's oldValue is always initially null here, causing the behavior property observer to call it's subscribers (eg the binding instance) resulting in sourceExpression.assign.

I wonder if we should remove the call in the controller and also set the BehaviorPropertyObserver's initial value according to what it's bound to.

@mbroadst
Copy link
Contributor Author

@jdanyow the last sentence sounds like a decent proposal, would that potentially break other expected behaviors?

@EisenbergEffect
Copy link
Contributor

A bindable has a particular behavior as part of the binding cycle of a component:

  • If the bind callback has been implemented on the component, then the bindable's callback should not be called because the developer will recieve the notification via the general bind callback.
  • If the bind callback has not been implemented on the component, then the bindable's callback must be called when the initial value is set, otherwise the developer has no way to respond to the initial value of the property.

@EisenbergEffect
Copy link
Contributor

@jdanyow Are you suggesting to have a second implemention, similar to call but that doesn't notify the subscribers?

@EisenbergEffect
Copy link
Contributor

I'm not sure that would work...when would the subscribers be notified? Consider one component that binds to another component's property. That subscriber needs to be notified of the initial value.

@mbroadst
Copy link
Contributor Author

@EisenbergEffect so you're saying the way to avoid the "initial bind" behavior is to implement bind on the VM of the bindable? That doesn't seem to be the case here, in fact in my personal project I have:

  bind(bindingContext, overrideContext) {
    this.$parent = bindingContext;
  }

so that I can use the VM as a parent for a sub-dialog

@jdanyow
Copy link
Contributor

jdanyow commented Dec 13, 2015

@EisenbergEffect makes sense, so I guess we have no choice but to evaluate the source expression and compare it to the new value.

This line:

this.updateSource(newValue);

Would become:

if (this.sourceExpression.evaluate(...) !== newValue) {
  this.updateSource(newValue);
}

sound good?

@EisenbergEffect
Copy link
Contributor

What do you think would be the impact of that? It's only going to affect two-way bindings correct? So, that shouldn't be a perf issue. Am I following correctly?

@jdanyow
Copy link
Contributor

jdanyow commented Dec 13, 2015

right- two-way bindings only, shouldn't be an issue.

@EisenbergEffect
Copy link
Contributor

ok, sounds good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants