templating icon indicating copy to clipboard operation
templating copied to clipboard

Replaceable templates not working across multiple levels

Open Mobe91 opened this issue 7 years ago • 10 comments

Replaceable templates accross multiple levels as shown in the following sketch are not working. The replaceable template in tab.html gets replaced with empty content.

tab.html

<template>
  <div class="title">Tab-Title</div>
  <template replaceable part="tab1-content"></template>
</template>

tab-control.html

<template>
  <tab>
    <template replace-part="tab1-content">
      <template replaceable part="tab1"></template>
    </template>
  </tab>
</template>

app.html

<template>
  <tab-control>
    <template replace-part="tab1">
      <form>...</form>
    </template>
  </tab-control>
</template>

Neither works this:

tab.html

<template>
  <div class="title">Tab-Title</div>
  <template replaceable part="tab1-content"></template>
</template>

tab-control.html

<template>
  <tab>
    <template replaceable part="tab1"></template>
  </tab>
</template>

app.html

<template>
  <tab-control>
    <template replace-part="tab1">
      <template replace-part="tab1-content">
        <form>...</form>
      </template>
    </template>
  </tab-control>
</template>

Mobe91 avatar Jun 13 '18 11:06 Mobe91

At the moment replaceable parts are single level.

StrahilKazlachev avatar Jun 13 '18 11:06 StrahilKazlachev

@StrahilKazlachev the first example is actually designed to be single level if the templates are replaced top-down.

Mobe91 avatar Jun 13 '18 14:06 Mobe91

By single level I meant direct. In the first example you are targeting from App in <tab>, through <tab-control>. This just isn't supported.

StrahilKazlachev avatar Jun 13 '18 14:06 StrahilKazlachev

Also looking for this feature! https://discourse.aurelia.io/t/render-template-within-template-within-element/2221

JoshMcCullough avatar Feb 04 '19 13:02 JoshMcCullough

I'm uncertain whether we can support this feature. Consider the following example:

a.html

<template>
  <div replaceable part="part-1">
    <div replaceable part="part-1a">Part 1a</div>
    <div replaceable part="part-1b">Part 1b</div>
  </div>
  <div replaceable part="part-2">
    <div replaceable part="part-2a">Part 2a</div>
    <div replaceable part="part-2b">Part 2b</div>
  </div>
</template>

What we are seeing here is 2 levels deep replaceable templates, with part="part-1" and part="part-2" Now suppose use this custom element in the following way: app.html

<template>
  <a>
    <template replace-part="part-1">...</template>
  </a>
</template>

The moment we replace part="part-1" in a.html with <template replace-part="part-1">...</template> from app.html, we lose all information about the replaced destination, because the parent <template replaceable part="part-1"> is lost, thus all the children of it:

<div replaceable part="part-1"> <!-- if this is replaced -->
  <!-- all the following are gone, no trace of what to replace inside -->
  <div replaceable part="part-1a">Part 1a</div>
  <div replaceable part="part-1b">Part 1b</div>
</div>

This is like assigning a new object to a property, there is no way to know what old properties there were for reconciliation.

In case that we know all to-be-replaced parts of the destination, and all provided parts to replace them, what should be done with non-replaceable parts?

a.html

<template>
  <div replaceable part="part-1">
    <!-- what should be done for this -->
    <h1>Not replaceable 1</h1>
    <div replaceable part="part-1a">Part 1a</div>
    <div replaceable part="part-1b">Part 1b</div>
  </div>
  <div replaceable part="part-2">
    <!-- what should be done for this -->
    <h1>Not replaceable 2</h1>
    <div replaceable part="part-2a">Part 2a</div>
    <div replaceable part="part-2b">Part 2b</div>
  </div>
</template>

For the above template, maybe part-1, part-1a, part-1b, part-2, part-2a, part-2b can all be made to work, what can be done with the two <h1/> elements once we want to replace both part-x and part-xa, part-xb?

thoughts ? @Mobe91 @StrahilKazlachev @JoshMcCullough @fkleuver @EisenbergEffect

bigopon avatar Feb 05 '19 09:02 bigopon

@bigopon If you add a (failing or passing) test for it in the vNext repo I can debug and see what might be needed to make it work, or establish whether it's possible at all (and give solid reasoning why not, if not).

fkleuver avatar Feb 05 '19 13:02 fkleuver

@bigopon If you have chosen to replace part-1, in your example, you have specifically chosen to "not care about and replace" the entire contents, including the other replaceable parts. IMO that is completely fine, and the correct result. If you intended to replace only the inner parts, you could have done that as well. This allows for the most flexibility.

Additionally, you added the <h1> which is "not replaceable" however, it is completely replaceable since it resides within a replaceable template. So it never was actually "not replaceable" in your example. The two <h1> elements should be replaced if the user is providing a replacement for the containing replaceable part.

JoshMcCullough avatar Feb 05 '19 15:02 JoshMcCullough

@fkleuver @JoshMcCullough I would imagine something like this

foo.html

<template>
  <div replaceable part="part-1">
    <h1>Not replaceable 1</h1>
    <div replaceable part="part-1a">Part 1a</div>
    <div replaceable part="part-1b">Part 1b</div>
  </div>
  <div replaceable part="part-2">
    <h1>Not replaceable 2</h1>
    <div replaceable part="part-2a">Part 2a</div>
    <div replaceable part="part-2b">Part 2b</div>
  </div>
</template>

Case 1: I want to replace part-1 (or part-2, simplified to only part-1):

app.html

<a>
  <template replace-part="part-1">
    ... i don't care what is in side part-1 in the destination. this is gonna be the content
  </template>
</a>

Case 2: I want to replace part-1/part-1a:

<foo>
  <template replace-part="part-1/part-1a">
    ... i don't care what is inside part-1 in the destination.
    ... i don't care what is inside part-1a in the destination
    ... i don't care what is inside part-1b in the destination
    ... This is gonna be the content of part-1a in part-1
  </template>
</foo>

Case 2: I want to replace part-1/part-1a and part-1/part-1b:

<foo>
  <template replace-part="part-1/part-1a">
    ... i don't care what is inside part-1 in the destination.
    ... i don't care what is inside part-1a in the destination
    ... i don't care what is inside part-1b in the destination
    ... This is gonna be the content of part-1a in part-1
  </template>
  <template replace-part="part-1/part-1b">
    ... i don't care what is inside part-1 in the destination.
    ... i don't care what is inside part-1a in the destination
    ... i don't care what is inside part-1b in the destination
    ... This is gonna be the content of part-1b in part-1
  </template>
</foo>

Additionally, you added the <h1> which is "not replaceable" however, it is completely replaceable since it resides within a replaceable template. So it never was actually "not replaceable" in your example. The two <h1> elements should be replaced if the user is providing a replacement for the containing replaceable part.

@JoshMcCullough Yes, it was there to demonstrate that we can't conditionally pick what to be replaced, when we provide both parent-replaceable and child-replaceable.

bigopon avatar Feb 05 '19 23:02 bigopon

So I wouldn't think you'd use replace-part="part-1/part-1a", but rather simply replace-part="part-1a.

I think all of your examples are correct in your latest comment. And it's up to the user to decide if they are replacing the outer or inner parts in this case. Aurelia could potentially raise an error/warning if a user tried to do this )using your example) ...

<!-- example to cause an error -->
<foo>
  <template replace-part="part-1">
    ...new content for part 1
  </template>

  <template replace-part="part-1a">
    ...new content for part 1a
  </template>
</foo>

ERROR: Cannot replace both parts "part-1" and "part-1a", one is nested within the other.

JoshMcCullough avatar Feb 06 '19 02:02 JoshMcCullough

@bigopon With regard to your example I would expect this to behave like @JoshMcCullough described.

But I don't see how your example relates to my original one because you don't really have nested templates but rather just nested replacement points within a single template..

While I understand that multi-level replacement is not supported right now I feel that it should be possible to support it by just processing the replacement points top-to-bottom and by reprocessing each intermediate replacement result. When I apply such an algorithm to version 1 of my original example, I end up with the correct end result:

  1. Process app.html
  2. Instantiate the tab-control template which yields a logical intermediate view of
<template>
  <tab-control>
    <tab>
      <template replace-part="tab1-content">
        <template replaceable part="tab1"></template>
      </template>
    </tab>
  </tab-control>
</template>
  1. Apply replacements for part tab1 defined in app.html which yields
<template>
  <tab-control>
    <tab>
      <template replace-part="tab1-content">
        <form>...</form>
      </template>
    </tab>
  </tab-control>
</template>
  1. Now we need to instantiate and replace again - instantiate tab with result
<template>
  <tab-control>
    <tab>
      <div class="title">Tab-Title</div>
      <template replaceable part="tab1-content"></template>
    </tab>
  </tab-control>
</template>
  1. Apply replacements for part tab1-content defined in the intermediate result of step 3 which yields the expected end result
<template>
  <tab-control>
    <tab>
      <div class="title">Tab-Title</div>
      <form>...</form>
    </tab>
  </tab-control>
</template>

Mobe91 avatar Feb 06 '19 06:02 Mobe91