-
Notifications
You must be signed in to change notification settings - Fork 310
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
Use prototype inheritance in each-binder #417
base: master
Are you sure you want to change the base?
Conversation
@jhnns This approach sounds very promising, and would solve a ton of issues around binding to root properties within nested views. The two potential problems you have listed aren't that problematic (compared to the benefits of this implementation), in my opinion. Reviewing / testing this now. |
Cool 👍 |
@jhnns This is close. But it seems like it doesn't solve the problem entirely. The main issue is the following: If you bind to a property within an iterated view that was originally set on the parent object (the prototype of the child object), it will redefine that property on the child object and observe changes on the child object's property instead of the parent object. See this demo: http://jsfiddle.net/znv28cmj/4/ The way that I've been trying to get things to behave is that if you change a root property on the parent scope, any bindings to that property in a nested scope should get the changes as well (it should point to the same property). Likewise, changing that property in the child view should also update it in the parent view (again, ideally they should point to the same property), but this is not as necessary as the former scenario (parent property updates the same property on child views). If we can get this to work, it would greatly simplify things. You should technically be able to bind your entire view / viewmodel as the scope object and all nested views would react the same way to properties on that view, as long as your models, collections and functions are reachable from your view object. rivets.bind(el, myView) |
Couple of solutions that I can think of:
|
Mhmm you're right, of course... I don't like the second solution because it is counter-intuitive and introduces a special syntax. But I don't get your first solution either – what is the difference between the scope and Imho there are these two solutions:
|
@jhnns The idea behind the first solution, is to never store properties more than once. Each view's scope (currently To visualize this: <div id="main"> <-- 1
<h1>{ model.title }</h1> // this will bind to 1's model.title
<div rv-each-item="items"> <-- 2
<p>{ item.name }</p> // this will bind to 2's item.name
<p>{ model.type }</p> // this will bind to 1's model.type
<rv-view <-- 3
ref="DetailsForm"
model="item">
</rv-view>
<p>{ test }</p> // doesn't exist in scope chain. bind to 1 or 2?
</div>
</div> Initializing the root view like this: rivets.bind($('#main'), { model: myModel, items: [myItem] }) Will create these views:
I suppose the problem with this is that inside your event handlers you would need to know about the scopes and where to find the properties you need access to. The other thing to consider is where do we bind if the property doesn't exist in the scope chain? Should we bind to the local scope or the root scope (like javascript does when you assign a variable without |
Ok, now I get it. That's exactly how prototype inheritance works under the hood. Maybe we should just use prototype inheritance but check on // no parent
var scope1 = {
model: myModel
};
// scope1 as parent
var scope2 = Object.create(scope1);
scope2.item = myItem;
scope2.index = 0; Reading values is no problem, it's just prototype inheritance: scope2.item; // returns myItem;
scope2.model; // returns myModel Writing values would work like this: while (scope.hasOwnProperty(key) === false) {
scope = Object.getPrototypeOf(scope);
}
scope[key] = value; Again, that wouldn't work in IE8 because there is no |
Oh an concerning your question: I think it should write to the current scope. JavaScript's behavior has been removed with strict mode for good reasons 😉 |
if you want a controller and a model to work on the div that has the |
In your example |
yes @jhnns my point is that I need the opposite of mixed concerns :) I do have a controller that is concerned about lists of controllers (and uses Nothing mixed, all decoupled and complexity scales fantastically well |
And what would |
The model of each children would be whatever they where configured to be. Typically the parent controller will instantiate them and tell what their model is. Usually a (sub)part of the parent model (but not necessarily). Answering your question with an example:
As example this is probably too simplistic and is tempting to think is just overhead to have one controller per |
It's possible to initialize all scope variables with custom values, but I think it's a good default to just inherit from the parent scope because that's very common. With inheritance you have the flexibility as you've described and the convenience as I suggested. |
@jhnns I agree, but I think it really depends on the particular iterated / child view and where the functionality of that view is coming from. Two scenarios that I see:
@sebastianconcept Does the second scenario describe your use-case more closely? |
angular's scope model is picking up this requirement pretty good |
@mikeric yes. The second scenario is what I wildly use. |
And what should we do next? 😁 |
Just joining in and giving my two cents. Keeping track of the parent view and falling back to a key path when a local one is missing would definitely be a good idea. This way there is also less pollution on each object in the iteration right? This would also lead to less complex user code when you have multiple nested iterations. |
@mikeric any updates on this? |
this can be fixed easily if you use object like this: http://jsfiddle.net/w23uLttu/ |
@Leeds-eBooks @Duder-onomy @blikblum @jccazeaux guys this is something what we should also pick up. This will also improve performance of UPDATE: by the way as @mikeric mentioned this can be used for every binding, not only in |
I never encountered this problem because I always used an global
@stalniy IMHO components are not concerned. If their model is not independent from parent, they won't be easily reusable. |
@jccazeaux I'd not say that. When PR with optional template option is merge it will be possible to extend default behavior of forms and inputs. This is probably the case where you would like to have access to parent variables inside |
@stalniy If component has access to parent, parent will have access to component. This will lead to side effects : if a parent defines same attribute as a component the values will be linked As an example, here are two fiddles.
A component can't know if it is defining an attribute already defined in parent (it doesn't know what will be the parent). This side effect has good chance to happen and can be really annoying. If a component needs something from parent, parent must send the data through attributes. |
@jccazeaux Probably I wasn't clear enough. What I'm saying is that it useful to extend default html elements' behavior, so for example <form name="myForm">
<input required rv-value="user.name" />
<span class="error" ng-show="myForm.invalid">error!</span>
</form> In this case you don't want to pass all the parent variables into form component, do you? P.S.: |
Indeed I didn't understand it right. |
This fixes a problem where arrays and objects where not updated inside the each-binding.
I've created a JSFiddle to demonstrate the problem: http://jsfiddle.net/hnodsc7n/1/
When the
items
-array is changed, the each-binding still holds a reference to the olditems
-array. This is because rivets just copies the properties from one model to another. However, this fix uses prototype inheritance. Thus every change on the super-model is propagated to the sub-model. Check out the difference: http://jsfiddle.net/znv28cmj/1/There are two potential problems with this fix:
Object.create()
which is not available in IE8. However, there is a polyfill for it (which I haven't included because imho libraries shouldn't bring their own polyfills).if (obj.hasOwnProperty(key))
it will not iterate over all properties anymore.All tests are running