-
Notifications
You must be signed in to change notification settings - Fork 25
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
--- | ||
title: Partial Template | ||
status: DRAFTED | ||
created_at: 2020-01-16 | ||
updated_at: 2020-01-17 | ||
pr: (leave this empty until the PR is created) | ||
--- | ||
|
||
# Patial Template | ||
|
||
## Summary | ||
|
||
Partial templates allow the ability to import html templates into another template via | ||
the normal variable syntax `{template}`. | ||
|
||
## Basic example | ||
|
||
```js | ||
import partialTemplate from './partialTemplate.html'; | ||
|
||
export default class Example extends LightningElement { | ||
partialTemplate = partialTemplate; | ||
} | ||
``` | ||
|
||
```html | ||
<template> | ||
Parent | ||
{partialTemplate} | ||
</template> | ||
``` | ||
|
||
## Motivation | ||
|
||
In other instances it was noted that large conditonal blocks of HTML could be more organized | ||
Templarian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
into seperate template files. | ||
|
||
## Detailed design | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Also, I think the fact that you need JS to use this makes me doubt that this is the correct approach. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, this is faulty for two main reasons:
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 // 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Would there be a huge jump in complexity between that and... <template lwc:partial={template} value={val}></template> There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
||
|
||
Templates can be rendered into a component one of two ways currently. | ||
|
||
- By matching the name of the component. Ex: `cmp.js` / `cmp.html` | ||
- By returning a default import in the `render() {}` method. | ||
- Ex: `import cmpTemplate from './cmp.html'` -> `render() { return cmpTemplate; }` | ||
|
||
A template is a constant and cannot be modified. Partials will inherit this same behavior. | ||
|
||
This simplifies things, but still may lead to complications in optimizations [unfamiliar with | ||
what this entails, so may need LWC context here]. Example: | ||
|
||
``` | ||
get dynamicTemplate() { | ||
return this.conditonal ? template1 : template2; | ||
} | ||
``` | ||
|
||
## Drawbacks | ||
Templarian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Why should we *not* do this? Please consider: | ||
|
||
- 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
the benfit if we're not able to optimize for this. | ||
- Recursion good/bad. | ||
|
||
> Note: Developers coming from other frameworks would expect this to work this way. | ||
|
||
## Alternatives | ||
|
||
Partials imported via the `template` tag and `lwc:partial` attribute. | ||
|
||
- 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
an `import`. | ||
|
||
``` | ||
// foo.html | ||
<template><p>Value: {value}</p></template> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
In which case, you will use:
|
||
|
||
// component.html | ||
<template> | ||
<h1>Before</h1> | ||
<template lwc:partial="./foo" value={myBeforeValue}></template> | ||
<h2>After</h1> | ||
<template lwc:partial="./foo" value={myAfterValue}></template> | ||
</template> | ||
``` | ||
|
||
This syntax greatly cuts down on what would be possible and introduces verbose | ||
conditional blocks of `if:true` that could lend themselves to unnecessary rerenders. | ||
|
||
## Adoption strategy | ||
|
||
Since this is not a breaking change and purely an enhancement to an existing syntax developers | ||
adoption will be optional. If they decide to reorganize existing components they'll go in with | ||
expectations partials are the way going forward to make complex templates more readable. | ||
|
||
In the examples above we may want to recommend reoganization of existing components if one was | ||
currently heavily nesting components. | ||
|
||
# How we teach this | ||
|
||
For any developer coming from JSX environment this pattern is already normal practice. Showing a | ||
simple example is enough to explain how partials work. | ||
|
||
Currently a developer would assume this syntax would include a partial and not `.toString()` the | ||
JS template function. | ||
|
||
# Unresolved questions | ||
|
||
- As with all feature this will depend heavily on performance. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
There was a problem hiding this comment.
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: