Skip to content
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

RFC: Partial Template #24

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Conversation

Templarian
Copy link

@Templarian Templarian commented Jan 16, 2020

Rough draft for Partial Template. I'll expand on this as I have time.


Rendered

In other instances it was noted that large conditonal blocks of HTML could be more organized
into seperate template files.

## Detailed design
Copy link
Contributor

@caridy caridy Jan 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The biggest question for me here is what is the scope of the partial? can it be controlled? or we assume that the template have access to the component instance? To be clear, I'm talking about {x} inside the partial, what does it do? is that equivalent to {x} in the importer? What if you the template inside an iteration, what is the scope of the partial? Keep in mind that today we use the JS scoping to resolve the bindings available for a block of compiled template?

Also, I think the fact that you need JS to use this makes me doubt that this is the correct approach.

Copy link
Author

@Templarian Templarian Jan 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would 100% assume it would work the same as if the partial's content had been pasted in.

template = 
<template>Hello {name}!</template>

class { template = importedTemplate; name = 'world'; }
<template>{template}</template>

Would result in:
Hello world!

Same would be the case anywhere the {template} is placed picking up any scoped variables.

Need to look into how templates are processed now to better understand how this could work. The alternative being templates are compile time placed in, but that limits what they can do and would be mainly for splitting up templates code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, this is faulty for two main reasons:

  1. it is not a reusable partial because you don't have a way to specify what should be accessible to it.
  2. it is impossible for the compiler (at its current state) to invoke another function (compiled partial) while providing the same JS scope of the caller.

While 2 is just a technical limitation that we can attempt to figure, I believe 1 is an ergonomic issue that is a deal breaker here, let me put an example:

// foo.html
<template><p>Value: {value}</p></template>

// component.html
<template>
      <h1>Before</h1>
      {foo}
      <h2>After</h1>
      {foo}
</template>

I'm trying to reuse a piece of the UI so I don't have to duplicate it in my template, but also I don't have to worry about the two fragments to go out of sync. This is clearly a use-case for partials, yet it doesn't work because the partial expects value to be accessible, but I have two values in the caller that I will like to display in two different places. That's why I think the scope MUST be specified, e.g.:

// foo.html
<template><p>Value: {value}</p></template>

// component.html
<template>
      <h1>Before</h1>
      <template lwc:partial="./foo" value={myBeforeValue}></template>
      <h2>After</h1>
      <template lwc:partial="./foo" value={myAfterValue}></template>
</template>

It also provides some declarative only syntax where the value of the lwc:partial directive doesn't have to be an imported value in JS if the partial is relative.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the alternative section with the example above.

That greatly simplifies the usage and allows more flexibility with template reuse. Assuming one could pair that with if:true/if:false.

Would there be a huge jump in complexity between that and...

<template lwc:partial={template} value={val}></template>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that work for events as well? Let's assume that my partial has a button and I'd like to trigger an action onclick. How can I assign an action? Is it something like this:

<template lwc:partial="./foo" onbuttonclick={myHandler}></template>

<template>
  <button onclick={onbuttonclick}>Mybutton</button>
<template>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@priandsf Assuming when it compiles down it has a way to wire up the scope it probably could do the same for events. Not really familiar enough with how the internals work though (just assuming it's a compile time in this case).

(this proposal was put on hiatus, but still something that would be beneficial)


- Introduce `lwc:partial="./file.html"` for compiled time content.
- Scoped `{variables}` within templates passed via attributes.
- Question: Would `lwc:partial={templateUrl}` not work? What about `lwc:partial={template}` via
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should work fine I believe, but @diervo will have to look at this.


```
<template lwc:include="./template.html"></template>
// foo.html
<template><p>Value: {value}</p></template>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are more details missing here, what you can pass into a template tag are attributes, what you can use in {something} are attributes, basically:

<template lwc:partial="./foo" some-value={myBeforeValue}></template>

In which case, you will use:

<template><p>Value: {someValue}</p></template>

Copy link
Member

@pmdartus pmdartus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to better understand the motivation of this proposal. The only framework offering a templating system that also supports partial is Ember and they are currently deprecating its usage: https://github.com/gavinjoyce/rfcs/blob/gj/deprecate-partials/text/0000-template.md. The general feedback is to use components over partials.

I would be really cautious before introducing such API in LWC.


# Unresolved questions

- As with all feature this will depend heavily on performance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there will be any performance penalty with this proposal, you can remove this unresolved question. Partial templates will just be another function invocation returning an array of virtual DOM nodes that will be inserted into the parent virtual DOM tree. This doesn't have any implications for the diffing algo.


- Static analysis. Analyzing the template can give us a lot of information, but with something
like this, it makes it more difficult.
- Conditionally swapping out partial templates could be performantly bad in themselves negating
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious to understand what optimizations we are thinking about here, and how bad this can be on the performance of business apps. Do you have more details?

</template>
```

## Motivation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see other motivations:

  • component inheritance. Typically, a base component can have some variables fragments in its template, which can be overridden by an inherited component. A good example is a complex commerce product detail component where you only want to change how the price is displayed.
  • Repeaters. You can easily create a 'repeat' component that takes a template as a parameter and iterate through it.

@pmdartus pmdartus added the draft label Oct 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants