templating icon indicating copy to clipboard operation
templating copied to clipboard

ViewFactory does not invalidate cache after the factories template changed

Open itfourp opened this issue 7 years ago • 24 comments

I'm submitting a bug report

  • Library Version: 1.7.0

Please tell us about your environment:

  • Operating System: Windows 10

  • Node Version: 6.10.2

  • NPM Version: 5.3.0

  • JSPM OR Webpack AND Version JSPM 0.16.53

  • Browser: all

  • Language: all

Current behavior: Using custom elements in multiple repeats with view-cache enabled causes weird behaviour when different templates are used for each repeat and items are moved from one repeat to the other one. I guess the fragment of the cached view is not updated when the element was moved from one repeat to another.

This bug first occurred when I was using the oribella-aurelia-sortable library that uses a repeat with enabled cache to ensure touch-support is working correctly (see furthermore here and here).

Expected/desired behavior: For an example for both expected and current behaviour and how to reproduce please see here: https://gist.run/?id=2cd8a03e16f92ce64308f9ec841c3b70

  • What is the expected behavior? We could invalidate the cache when the template of the ViewFactory has changed so the ViewFactory is forced to re-create the view, but I am not sure if the will break touch-support yet again.

  • What is the motivation / use case for changing the behavior? To ensure the oribella-aurelia-sortable library can be used with touch support.

itfourp avatar Jan 04 '18 08:01 itfourp

@EisenbergEffect & @jdanyow It is probably a rare use case, but please have a look at this and the linked issue in aurelia-sortable plugin, when you will have some time.

Alexander-Taran avatar Mar 22 '18 11:03 Alexander-Taran

I think the issue is that the view caching mechanism isn't taking part overrides into consideration. @bigopon Interested in an interesting challenge?

@itfourp If you don't use the cache mechanism, does that cause you perf issues?

EisenbergEffect avatar Mar 22 '18 16:03 EisenbergEffect

@EisenbergEffect I don't think so, but since I need touch support and there is an issue with touch listeners and removed html elements I'm pretty much depending on the caching mechanism. I tried resolving the problem in the aurelia-sortable library itself, but that's pretty hackish and we decided to look for a better solution first.

itfourp avatar Mar 23 '18 10:03 itfourp

@bigopon @EisenbergEffect Any updates regarding this?

As @itfourp said if you need touch support this needs to be fixed. Do we know where in the view cache mechanism we need to fix this?

stoffeastrom avatar Apr 30 '18 16:04 stoffeastrom

I don't think anyone has had a chance to look into this deeply yet.

EisenbergEffect avatar Apr 30 '18 23:04 EisenbergEffect

Because the issue is in the repaceable part, which is nested inside repeated element, it's quite difficult to work out. Maybe we can fix getCachedView() to give it some extra arguments so it can determine if the cache view is going to be correct

bigopon avatar May 26 '18 08:05 bigopon

@EisenbergEffect What about we introduces a special attribute similar to view-cache, say separate-cache so each Repeat will be associated with different cache ?

  <div repeat.for='item of items' separate-cache>
    ...
  </div>

bigopon avatar May 29 '18 07:05 bigopon

I'd be willing to entertain this idea. It is an edge case, so I'm not sure it's worth it to re-write the entire system. But if we can enable a simple setting like this, then the few people who really need this can opt in.

EisenbergEffect avatar May 30 '18 00:05 EisenbergEffect

Maybe it should be a named cache?

EisenbergEffect avatar May 30 '18 00:05 EisenbergEffect

It's not a rewrite, I think it can simply be some extra arguments around caching and retrieving cache in view factory

bigopon avatar May 30 '18 00:05 bigopon

That seems ok. Do you want to have a go at it?

EisenbergEffect avatar May 30 '18 05:05 EisenbergEffect

I'll give it a shot after the current feature set

bigopon avatar May 30 '18 05:05 bigopon

I'm very happy you've already got some ideas to fix this issue. If you're interested I could set up another gist or a github project that makes use of touchy-stuff and you could use to test your solution on.

itfourp avatar Jun 04 '18 11:06 itfourp

@itfourp No rush and that'd be great 👍

bigopon avatar Jun 04 '18 12:06 bigopon

Sorry, I completely forgot to mention I'm done with the setup of the touch-demonstration project for this issue. Please see here. If anything does not work as expected feel free to tell me.

itfourp avatar Oct 30 '18 12:10 itfourp

@itfourp Nice. Thanks!

bigopon avatar Oct 30 '18 12:10 bigopon

@itfourp I'm unable to reproduce the issue as you can see from this demo https://dist-ouuggzgmue.now.sh/ I'm not sure where the differences are, but I followed the step you described.

bigopon avatar Nov 17 '18 07:11 bigopon

@bigopon I am sorry my instructions have been unclear. In this example I've already set the view-cache to 0 in the sort-container.html to show the touch issues that occur with disabled view caching. To reproduce the behavior I reported here please just re-enable the caching.

itfourp avatar Nov 17 '18 08:11 itfourp

@bigopon I've updated my example repository here. There now are two views showing the problem with caching and without - hope it helps.

itfourp avatar Feb 11 '19 14:02 itfourp

Thanks @itfourp , I haven't got around to fix the issue, but I'll try to summarize the issue first for some discussion, before starting that work.

The normal usage of repeat + view-cache combo that doesn't cause issue:

Supposed we have the following as template of custom element named foo:

<!-- foo.html -->
<template>
  <div repeat.for="item of items" view-cache="*">
    ... bunch of content
  </div>
</template>

In this case, the template of repeat template controller doesn't let any foreign content leaked into it. So when repeat caches views in and retrieves views out, there is no issue.

The problematic usage of repeat + view-cache combo that causes the issue:

Suppose we have the following as template of a custom element named foo:

<!-- foo.html -->
<template>
  <div repeat.for="item of items" view-cache="*">
    <div replaceable part="part-1">

    </div>
  </div>
</template>

And it will be used in 2 places inside app root like following:

<template>
  <foo>
    <template replace-part="part-1">
      ... bunch of content 1
    </template>
  </foo>
  ... bunch of content
  <foo>
    <template replace-part="part-1">
      ... bunch of content 2
    </template>
  </foo>
</template>

What will happen at runtime is:

  • both usage of <foo> will cause <div replaceable part="part-1"> to be replaced with either <template replace-part="part-1"> content 1 or <template replace-part="part-1"> content 2. And this is permanent replacement.
  • when repeat caches in, it caches views that have nested content that has been replace by either <template replace-part="part-1"> content 1 or <template replace-part="part-1"> content 2, without trying to distinguish them.
  • when these caches are retrieved, depends on the order, replace-part="part-1" > content 1 may get the view that was from replace-part="part-1"> content 2 and vice versa.

This issue comes from the fact that no matter how many instances of repeat there are, they all share the same underlying ViewFactory instance. So to fix this bug, we need to have a way to tell the repeat that it should not use the default cache, via some attribute (API is for discussion):

<!-- foo.html -->
<template>
  <div repeat.for="item of items" view-cache="*" separate-cache-for-each-repeat>
    <div replaceable part="part-1">

    </div>
  </div>
</template>

This requires new APIs for ViewFactory to enable the ability to return views to different caches set. I think this is a simple extension, as by default every view will just return to default cache, unless explicitly told what cache it should go to.

thoughts? @EisenbergEffect @fkleuver @itfourp @stoffeastrom @Alexander-Taran

bigopon avatar Feb 12 '19 06:02 bigopon

I'd like @fkleuver's thoughts on this, particularly as it pertains to vNext. If possible I think we should endeavor to solve this for vNext and backport to vCurrent so that our solution here is forward-compatible and so that this usage scenario is captured and handled going forward (no regression in vNext).

I'm not particularly fond of the API above 😏 So, some further design thinking on that would be nice. Ideally, the option wouldn't need to be specified at all. We'd just detect that there's a replaceable part involved and automatically create a named cache to handle this behind the scenes.

EisenbergEffect avatar Feb 12 '19 23:02 EisenbergEffect

For a sortable/draggable repeater in vNext, you would use keyed mode. This reorders the elements instead of re-binding them and thereby retains the element state (event listeners, css, etc). That simplifies this situation a lot.

For caching views in keyed mode, my immediate thought is: KeyedViewFactory. Repeater passes the key to factory.create, and factory will only retrieve a cached view if one already exists for that one key. This inherently accounts for anything going back-and-forth between different repeaters (a common use case; dragging elements from one list to another).

Regarding the edge case with nested replaceable, this problem might not exist in vNext in the first place. Detaching only happens for root elements, so nested views simply stay in the DOM of their (cached) parent which, when un-cached, is appended back to the DOM as one big whole again.

To make all of this work in vCurrent I do see the possibility of backporting keyed mode (this might even be easier to do in vCurrent than it was in vNext because there is less lifecycle order/timing stuff to account for) as well as having a KeyedViewFactory. For the replaceable problem we would just have to experiment a little with various ways to propagate the key of a parent down to child views so they're locked together as a set. I think this would apply to any nested views, not just replaceable

Without keyed mode, I'm not sure if we should even try. This is the primary use case for keyed mode. That inherently brings with it a key. I think that takes away a fair amount of design uncertainties.

@bigopon maybe either you or me should port @itfourp 's repro to vNext and see if it already works or not. In any case vNext is much closer to being able to deal with this already than vCurrent is, so we should look there first imo.

fkleuver avatar Feb 13 '19 01:02 fkleuver

@bigopon maybe either you or me should port @itfourp 's repro to vNext and see if it already works or not. In any case vNext is much closer to being able to deal with this already than vCurrent is, so we should look there first imo.

I'll do this soon.

bigopon avatar Feb 13 '19 01:02 bigopon

I think I've stumbled upon something related to this issue, but for now managed to work around it. Just wanted to know if there has been anything done about this that perhaps I could try, before elaborating more on my specific case (which involves both sorting [through a value converter] and replaceable parts in a probably pretty advanced way... 😬). In short, the issue is with checked bindings for a set of radiobuttons inside one of those replaceable parts, in each of the sorted items).

ping @bigopon

deap82 avatar Mar 16 '22 15:03 deap82