BorisMoore / jsviews

Interactive data-driven views, MVVM and MVP, built on top of JsRender templates
http://www.jsviews.com/#jsviews
MIT License
857 stars 130 forks source link

how to identify the bound view-item life-cycle when observable is changing? #342

Closed KrishnaPG closed 8 years ago

KrishnaPG commented 8 years ago

Scenario:

Goal: to attach incoming / outgoing animations to the item, that get triggered automatically when the item is about to be removed / replaced

In other words, for an item I would like to know when it is getting displayed for the first time (so that I can attach some local DOM properties to it) and then know when it is getting destroyed (so that I can do some cleanup).

BorisMoore commented 8 years ago

I hope to add documentation for the life-cycle events at some point before declaring the V1 release.

Events include onBeforeChange onAfterChange onUpdate onBeforeLink onAfterLink onDispose. You can see some code examples in https://github.com/BorisMoore/jsviews/blob/master/test/unit-tests/tests-jsviews.js - if you search for those event names.

To do the animation scenario you suggest, you can use onBeforeChange and onAfterChange, as shown in this sample which I just created...

https://jsfiddle.net/BorisMoore/vm9ovhm8/

KrishnaPG commented 8 years ago

Thank you much @BorisMoore The example helps clarify many things.

Though, it looks like there is some issue with the insert / remove behavior in that example - if you try to repeatedly insert, remove in random order, after couple of times no further insertions or removals are happening. List doesn't change any further. Observed this behavior in both Chrome and Edge.

Another point is: how to do complete "refresh" of the items.

I tried to modify your example to do complete array replacement: https://jsfiddle.net/KrishnaPG/vm9ovhm8/4/ But if you click on the 'refresh' the whole array does not change.

Also, after clicking on the 'refresh' try clicking on 'remove' and then 'insert' you can see the freezing problem.

KrishnaPG commented 8 years ago

To give a little bit context, what I am after is: similar to the quicksand filters behavior (not the UI part, but the logical "diff" part)

Say you have an array of items. After sometime, it has to be replaced with a new set of items. And this new set is not necessarily completely new - some of the old items may still be there (or may be not).

Something like: new items = untouched_old_items + new_additions - removals

The goal is to have any 'untouched' old items do nothing, the 'to be removed' old items to fade away and the 'new additions' to nicely fade-in to occupy any empty spaces (moving the 'untouched' old items up or down the array index as required by the sort criteria, if any).

While this sounds slightly complex requirement - unfortunately this is so common requirement with REST based item retrievals these days (say feeds, user-based content filters etc.)

The reason I am saying this is: if we go with the 'item by item' change handling approach as outlined in the example, then it would create performance complexities.

Rather the view should take the new set and compute the difference with the old set of items, and in a single go trigger the animations for all 'new' and 'old items' in a single call or batch. Would that kind of thing possible?

BorisMoore commented 8 years ago

That jsfiddle sample is intended just to show how the onBeforeChange and onAfterChange work, but it is a simple POC, not a real implementation for the animation scenario. In fact, by returning false from onBeforeChange it is cancelling on the DOM operations, so that the deleted row is not actually yet deleted, but instead starts its animation. However the corresponding data array has still been modified, and it is only when the animation completes that the data and the views are again correctly in sync. So the implementation is in no way robust when you have a 'reentrant' scenario, of a second click before the animation is complete. In that case you are doing an array operation on data that is not correctly in sync with the views, so this will be likely to fail.

https://jsfiddle.net/BorisMoore/vm9ovhm8/5/ is an updated version to suggest a possible approach towards a robust version, but it does not support refresh(). For that we need to also handle events with eventArgs.change === "move" (since refresh is automatically converted to a sequence of "move", "insert" and "remove" operations).

The merge() operation for compiled View Models already does compute the difference between old and new, and create an optimized elementary operations (again, "move", "insert" and "remove"). So in principle one could use merge() (with identity determination for each item) and then animate the elementary operations. But the difficulty is again the necessity for asynchronous treatment of removal of rows, so that they removal is deferred until the animation completes. (Same issues as in the jsfiddle). So this needs some work to get a complete working design... Hopefully possible with the current JsViews implementation - but let me know if you explore this and see any API changes that might make the scenario easier to acheive, or more performant...

See http://www.jsviews.com/#viewmodelsapi@merge and http://www.jsviews.com/#jsvviewmodelsapi@mergeadvsample

BorisMoore commented 8 years ago

I'll close this for now. (Reopen, or let me know, if you see work that you feel needs to be done before v1.0 is released, related to the above discussion.)