lit-element: litElement doesn't support nested components
It doesn’t seem litElement allows you to nest elements in the HTML page itself.
For example If I wanted to do this:
<parent-element>
<nested-element></nested-element>
<nested-element></nested-element>
</parent-element
And I declared these elements like so:
class ParentElement extends LitElement {
constructor() {
super();
}
render() {
return html`<div class="parent"><slot></slot></div>`;
}
}
customElements.define('parent-element', ParentElement);
class NestedElement extends LitElement {
constructor() {
super();
}
render() {
return html`<div class="nested"></div>`;
}
}
customElements.define('nested-element', NestedElement);
It ignores the nesting and prints them like:
<parent-element>
<!---->
<div class="parent"><slot></slot></div>
<!---->
<nested-element>
<!---->
<div class="nested"></div>
<!---->
</nested-element>
<nested-element>
<!---->
<div class="nested"></div>
<!---->
</nested-element>
</parent-element>
So whilst it keeps the nested components it’s not honouring that the HTML itself should be nested inside each other.
In fact, if I remove the <slot></slot> it still prints it exactly the same… so it seems litElement doesn’t care about the nesting of the components…
Does litElement support nested components? Or is this an error in the code?
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 1
- Comments: 16 (8 by maintainers)
@justinfagnani How can I nest Web Components but allow them to inherit the styling from the page that they are on? Without the
createRenderRootit blocks the CSS from the host page from affecting the components which isn’t what I want. We really need the page CSS to be able to bleed into the components and we also need to be able to nest components.This has nothing to do with web components or lit-element, it’s simply how HTML and the DOM work.
In most js frameworks, the framework has control over the entire tree so it can render things in different places as it sees fit. Web components are actual dom nodes, so anything they render goes into that dom node. Rendering web components is async, so it cannot assume anything about it’s parent or child nodes.
In the browser the slotting issue is solved with shadow dom.
You can nest web components just fine:
This will render element-a, with element-b inside. However, if
element-asays:it will remove
element-b.The default behavior of
lit-elementis to render into a shadow root, in which it case it doesn’t overwrite it’s child nodes. In your case you changedcreateRenderRootto returnthis, so you’re always overwriting your child nodes.You could do something like this:
this way element-a will render into a wrapper div, and won’t overwrite the other child nodes.
If you want to wrap the content of element-a around the “slotted” content given by the parent, it gets pretty complex. Web components render async, so the child nodes could come in at any later time, and the parent could make changes which need to be tracked. Moving around live nodes like that isn’t a good idea.
However, if both elements use lit-html, you could set the content to render as a property:
This is essentially how most frameworks work as well, just with a more declarative syntax.
I can’t imagine how Light DOM rendering would be conceived as a misfeature for anyone who seeks to build entire apps or sites with a pure LitElement approach and not least for the reason @iamdriz states. I would prefer to offer a
static light = truesetting to enable this feature along with an upfront explanation of its consequences with regards toslotsupport and style encapsulation. Until components may offer an interface for controlled bleeding of the context cascade, official support for Light DOM components will only make the library stronger since CSS encapsulation is simply not always desired, at least not by everyone, and would in any case often be better served with<style scoped>instead of manhandling of the Shadow DOM. I having fun with LitElement both in and out the Shadow DOM and it has certainly restored faith in my career path.the root of this issue, and lit/lit-element#533, is that developers are frustrated by the shadow-dom’s lack of options for application-level themeing
our current options for application-level themeing:
→ downside: tedious
→ downside: hacky
future solutions for application-level themeing:
adoptedStyleSheetsfeature via “constructible stylesheets” so components can adopt stylesheets from the parent page→ downside: not available, no polyfill
→ downside: not available, no polyfills
I wonder if combining the two strategies above you could arrive at a generic ‘Styled component’ that would inject a theme into whatever LitElement you provide, eg:
styled.js:
You can also use themes in an inheritance pattern.
For those who want to “pierce” or “inject” a broader theme stylesheet into all instances of a LitElement, there’s another option that hasn’t been mentioned so far: LitElement’s static
stylesgetter can return anArrayinstead of a singleCSSResult.So, instead of this:
You can export something like this:
Then you can use your globally-themed component like: