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

Support for custom two-way bindings #2

Open
mbest opened this issue May 21, 2013 · 7 comments
Open

Support for custom two-way bindings #2

mbest opened this issue May 21, 2013 · 7 comments

Comments

@mbest
Copy link

mbest commented May 21, 2013

As @rniemeyer mentioned in the comments of your blog post, when using this plugin, the bindings aren't provided with the observable, only with the value of the property. The built-in bindings use Knockout's internal _ko_property_writers feature to be able to write back to the property, but custom bindings may not be able to use that, especially if they use an options object.

@rniemeyer
Copy link

The other problem that I ran into when I had previously looked at this type of thing was the issue of passing by reference vs. by value. Lots of KO plugin code assumes that you can pass observables around as an argument or set a variable equal to it (or set sub-properties on it). With ES5 properties, you would lose the getter/setters (http://jsfiddle.net/rniemeyer/Mvw6H/) when doing that and instead would always need to interact with the property off of the parent object.

@SteveSanderson
Copy link
Owner

The built-in bindings use Knockout's internal _ko_property_writers feature to be able to write back to the property, but custom bindings may not be able to use that

Yep - very valid point. Maybe it's a good reason for us to look at making a public property writers API in KO v3.

especially if they use an options object

That may be a reason not to use options objects in those particular cases, now that v3 handles independent bindings properly anyway.

Lots of KO plugin code assumes that you can pass observables around as an argument or set a variable equal to it (or set sub-properties on it)

Also very true. A couple of possibilities:

(1) In KO 3, we could make it easy for bindings to receive the raw, unevaluated text they are being bound with. An extra option on the ko.bindingsHandler.someBinding object could trigger a preprocess step where we wrap the bound value in quotes, so the binding always receives a string. For example, if somebinding had this option on, then:

data-bind="somebinding: myProperty"

... would be preprocessed so that the binding receives the value as if the developer wrote:

data-bind="somebinding: 'myProperty'"

... and then the binding can read/write viewModel[propertyName], or can access ko.getObservable(viewModel, propertyName).

This doesn't help with options objects, but is natural and simple in the non-options-object case.

(2) As well as creating a wrapped property called someProperty, ko.track could also add a property called _someProperty that is the underlying observable. Then the developer can work with sub-observables on _someProperty. Doesn't help much with bindings though, unless you bind to _someProperty (which would be weird).

@rniemeyer
Copy link

My other thought was to use a binding provider that would supply the bindings with actual observables. It would be kind of like doing the opposite of ko.toJS on what was passed to the binding, where it actually returns an object with the ES5 props replaced with real observables. For passing an observable directly, it seems like this would work nicely. However, when passing objects there might be issues with providing the binding with a new object (copy with observables) rather than a reference to the exact object that they intended to pass.

@mbest
Copy link
Author

mbest commented May 21, 2013

I like the _someProperty idea, although it could be made even more obvious with somePropertyObservable. It seems this might also eliminate the WeakMap dependency.

@brianmhunt
Copy link

Could this issue be the cause of brianmhunt/knockout-secure-binding#23?

@aldendaniels
Copy link

FWIW, I'm working around this limitation like this:

var ViewModel = function() {
   this.field = '';
   this.getObservable = function(fieldName) {
      return ko.getObservable(this, fieldName);
   }.bind(this);
   ko.track();
}
<div data-bind="myTwoWayBinding: getObservable('field')"></div>

Not elegant, but it works.

@mateubo
Copy link

mateubo commented Jan 26, 2015

I would like to put my 2 cents in as well. When I started using ES5, I wanted it to be compatible with all the existing bindings I had, and also invisible for the developer. I wrote a custom preprocess function that kind of parses the original binding string to make the observable available in the binding:

prop1  -->  ($data.prop1__koObs__ ? prop1__koObs__ : prop1)
{ p1: prop1 }  -->  {p1:($data.prop1__koObs__ ? prop1__koObs__ : prop1)}
{ p1: prop1WithExtender.isValid() }  -->  {p1:($data.prop1WithExtender__koObs__ ? prop1WithExtender__koObs__ : prop1WithExtender).isValid()}
{ p1: { subp1: prop1 > 12, subp2: prop2.prop3.prop4 } }  -->  {p1:{subp1:prop1 > 12,subp2:(prop2.prop3.prop4__koObs__ ? prop2.prop3.prop4__koObs__ : prop2.prop3.prop4)}}

It works quite fine, yet to do so, I had to modify the library a bit to make the original observable available through a property named propertyName + "__koObs__" which is not nice, because for one thing it makes a mess in a viewmodel's property tree when you debug it later on.

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

6 participants