-
Notifications
You must be signed in to change notification settings - Fork 309
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
Make each-* routine compatible with collections #275
Conversation
👍 👍 to merge this. I had an issue with eaching over a collection (using a custom adapter), but eaching over collection.models with the default dot adapter causes the same problems. Upon removing a model from the middle of the collection, the on-* binders would not get correctly updated (even though the other things, such as rv-text would) I tried out this pull request and it fixes the issue. Thanks @ydaniv ! |
@ydaniv Thanks for taking the time to identify this and open a pull request. The implementation changes look reasonable so far, but It doesn't seem to maintain the proper ordering of an array. Notice how the order of the nodes is not consistent when changing Actually, the first node seems to be consistent with the first item in the array, but the rest of the items seem to go in reverse order. e.g. I'm also curious about the issues you were seeing with the current implementation (of updating the existing |
@mikeric Here are 3 minimal use cases. - This regards specifically the problems I rand into. I believe they are similar to @ydaniv , but they might not be identical. Both his pr, and rivets 0.6.5 solved my problem. Rivets 0.6.6 contains the problem. Basically the example is removing an item from an array using splice and using on click handlers. This is roughly the equivalent of binding to a Backbone collection and calling model.destroy on one of the models in the collection. This seems to be due to unbound click handlers.
0.6.6 - unexpected (and looking into it further - it looks like the custom handler is causing this - the custom handler is needed to handle binding to prototype methods - e.g. model.prototype.destroy - here is a minimal use case without the use of prototype methods and a custom handler, and all works as expected - http://jsfiddle.net/pajtai/d2pdw/ ) 0.6.5 - expected (this surprised me, I'll have to look into this more, and see whether our custom adapter works with this version) edit: yes, 0.6.5 but not 0.6.6 works with our custom bb adapter @ydaniv fork - expected |
@pajtai Ah, yes this is definitely an issue, however it's not at all related to the The issue is that when Here is a more isolated instance of the issue. http://jsfiddle.net/t2sxB/ This pull request does not actually fix that issue. It just worked in your case because this pull request is replacing the entire view for each iteration instead of calling A temporary solution is to use I will add a fix for this in 0.6.7 for sure though. |
@mikeric Thanks for the explanation! |
@mikeric thanks for pointing out the order issues, I've fixed it, added an extra test for that case and will update the PR. Note to self: don't use Regarding the issues I'm seeing, it's specifically about using The problem is that while Rivets may not care about the data each model contains, as long as it is in sync with the DOM, other components that hold references to specific models do care about the data in the model they are watching. I'm working now on preparing a fiddle that will demonstrate it better. |
@mikeric here you go, consider the following example of using Backbone views with Rivets. You might argue that views of Backbone and Rivets are mutually exclusive in an application (which I personally don't think so), but what's important here is the example of another component watching the collections' models: This example is using your latest code: Notice what happens after you add 3 items, then remove the middle one, and then click on the items to log their ID. The text created by the binding no longer matches the bound model. This example is using the dist you created from my PR: Here you can see that the bound model is left untouched by Rivets. This gets repeated in even more nasty ways when trying to use any kind of UI components that bind to the models in the collection. The main take away here is interoperability of Rivets with collections (be it BB, Spine, or any |
@pajtai ok the bug that you've outlined should be fixed now in master. Here is your example from above, but using 0.6.7. |
@mikeric any comments about this PR's original intent? Anything not clear that I can try better explain? |
@ydaniv Yep, just want to be sure that I got this right. I might be way off on this, and please let me know if I'm just misunderstanding the issue completely. But I think you might be confusing interoperability with wanting Rivets to assume responsibility where it shouldn't be. ProblemYou're using Rivets to bind most things in that iterated view (and it successfully keeps those things in sync), however you've decided to use a Backbone view to bind that This is why things are misaligned when your data changes — Rivets is updating things that it is in charge of, but your Backbone view is not. Solution if you must use two different methods for bindingYou need to make sure that your Backbone view (or any other means of binding) is observing and updating things in the same manner as Rivets. You can't tell Rivets to account for Solution using RivetsUsing The solution provided by this pull requestThis pull request kind of avoids the issue by not using I'm not opposed to merging this in, for example if it provides performance improvements. And then I guess as a bonus, it would provide a cop out for needing to define observers on your Backbone view when using I'm going to run some benchmarks and compare the two. |
@mikeric no, not confusing it. My point about interoperability is that if you change an object that doesn't belong to you, when you weren't asked to, then you're assuming authority over an action on an object that you weren't granted, or at least, were not expected to have taken. Ignore the use of Backbone.View, that was just a mean to make a quick demo. I don't just them myself, I actually need Rivets to be a data binding library for use with uijet. I'll explain my example in a more generic way:
So, back to my example, binding also to other actions, like Using So, yes, I say "don't use Should I produce another example? (: I can also get my TodoMVC example online on my fork if it helps. |
Has this been demonstrated in your fiddle? I don't see Rivets changing any of the objects that you've bound to that view. The only bound object that seems to have been changed is your
Sounds like the same assertion as above. Can you confirm that Rivets is actually doing this?
This is false. Rivets is not tampering with your models and/or references to them from other objects. Maybe it wasn't clear, but the reason why your view appears to be logging the incorrect Here's a modified version of your fiddle that displays the
If the issue has been demonstrated in your first example, then no, there should be no need. But if it hasn't, then yes that would certainly be helpful :P Seeing the TodoMVC app would be great for some context though. I'm also curious to check out UIJet. Sorry for all the back and forth on this, I'd really like to come to a consensus and get it resolved. I still haven't had the chance to run some benchmarks, but if this is in fact faster, then I'll definitely merge it in regardless of the issue being the fault of Rivets or not. |
OK... now I see what's happening. What you said about the So, yes that is not up to Rivets to maintain. However, this method does cause a lot of headaches when trying to bring in another component that needs to bind itself to the DOM. Thanks for putting up with me :P But do tell if this brings any perfomance improvements, which I doubt. |
@mikeric on second thought, I think there are some improvements in my implementation but it looks like it's still incomplete, and I think we can do much better. The main issue with current implementation is that it's only optimized for the case of adding/removing items at the end of the iterable. If you're removing/adding to the beginning or middle of the list it will change the DOM for every element in the list, starting from the changed element and on. I suggest to take another shot at this implementation and try do the extra work in JS so to minimize the number of times Rivets touches the DOM, which is the real performance killer. What say you? |
each-routines are always tricky... ^^ |
+1 for minimizing DOM rerendering in each-* |
I gave it a shot in PR #311. |
Current implementation overwrites objects in-place, so when another object (e.g. a UI component) references the object in the collection that's overwritten discrepancies start to appear between the state of that component and the object (model) in the collection it's referencing.
This implementation maintains the original objects while keeping the correct state and same index order of collection and DOM elements.
This means that the each-* routine can now be safely used with Backbone.js/Spine.js/etc collections and therefore, Rivets.js can become a genuine part of any SPA stack.
This implementation has been tested in a stack using Backbone.js and supports listening to the
add
,remove
andsort
events of Backbone's collections, with UI components referencing both the collection and each of its models.~Y