webc
webc copied to clipboard
How to use recursive (self-nested) WebC components?
TL;DR
A WebC component that can optionally contain itself (much like a ul
can contain other ul
s) is not generating the nested elements correctly.
Since the docs state that “WebC is HTML”, I did not expect a WebC component that is basically a glorified ul
to have this issue with nesting.
I am still learning subtleties of how data flows through WebC components, and it’s possible I’m missing something obvious.
(Added in Edit) Steps To Reproduce
- Checkout Zearin/webc-org-nested-depts
- Run
npm i
- Run
npm start
Test repo demonstration
https://github.com/Zearin/webc-org-nested-depts
The Components
I have a simple project where there are 3 components (all HTML-only). 2 of the 3 are special-case <ul>
elements, and the last one is a special-case <li>
element.
Components
<!--- dept-list.webc --->
<ul class="dept-list">
<dept webc:for="d of depts"
:@name="d.name"
:@tags="d.tags"
:@depts="d.contains"
></dept>
</ul>
<!--- dept.webc --->
<li class="dept">
<span class="name" @raw="name"></span>
<tag-list webc:if="tags" :@tags="tags"></tag-list>
<dept-list webc:if="depts"
:@depts="Array.from(depts)" <!--- or just depts; same outcome --->
></dept-list>
</li>
<!--- tag-list.webc --->
<ul class="tag-list">
<li webc:for="tag of tags" @text="tag"></li>
</ul>
The Problem
- ✅ The top-level list generates fine;
- ✅ Each top-level
dept
generatestag-list
fine; but - ❌ Any nested
dept-list
are not generating as they should.
Since these are all HTML-only components, I expected no custom elements to exist in the output.
Instead, the nested dept-list
elements are generating as empty <dept-list></dept-list>
elements. (Added in edit) Here is an example of the problem output.
@zachleat, @5t3ph, or @darthmall: I’ll gladly buy a coffee or three via sponsorship (of your choice) if someone can help me figure this one out!
Hey @Zearin,
I'm also figuring out webc right now so this may not be a valid answer but wouldn't the code snippets above create an infinitely recursing list since dept.webc
relies on dept-list.webc
and vice versa?
Additionally, looking at your repo, appears there's a typo here though I have no idea if that would make any difference.
Hi @sc0ttes,
No, it wouldn’t create an infinite loop because the data is not infinitely nested. I think it’s 2 or 3 levels deep at the most.
I appreciate your catching the typo, though! Thanks 🙏 ✅Fixed!
I've also come across this same scenario with @11ty/webc 0.11.0 and @11ty/eleventy-plugin-webc 0.11.1
Assuming since there's no follow up that you've not figured this one out yet right @Zearin?
Alas! No. No luck on my end. :)
Hi @Zearin, I'm facing the same issue, and it seems to be intentional behavior by the author (see ast.js). Unfortunately, self-nesting components are mandatory for our project. Do you have a workaround or perhaps another product to recommend ?
@maxdhn Alas, I haven't used WebC in months because of this issue. ☹️
@maxdhn Forgot to mention: I found this ebook incredibly helpful and informative in rolling my own web components (and easier than adopting another library).
Ran into this today and it is still a problem. I was trying to recreate my own eleventyNavigationToHtml (because that was generating incorrect code too). There's a way to take full control by doing it yourself in Nunjucks. Sadly, it looks like the suggestion to use the equivalent of nested calls to a Nunjucks macro simply doesn't work in webc.
I'm going to guess that this is because the dependency graph is not capable of circular dependencies, since it is doing more than the other systems like Nunjucks which mostly translates text into other text.
sigh Adding this to the list of long-term known issues with webc that I've personally run into in the last two weeks (over a half dozen things at this point).
In this case, you could get around this with a hack by using the webc method of exporting raw strings. This will only be practical if you know in advance what components are going to appear inside other components (knowing for sure any that happen to become recursive are handled manually.
So, in my case:
<ul webc:root="override" webc:if="(items ?? []).length > 0">
<li webc:for="item of items">
<a :href="item.url" @raw="item.title"></a>
<nav-list :@items="item.children"></nav-list>
</li>
</ul>
Becomes:
<script webc:type="js" webc:root="override">
const genList = (list) => {
if ((list ?? []).length <= 0) {
return '';
}
return `
<ul>
${list.map((item) => {
return `
<li>
<a href="${item.url}">${item.title}</a>
${genList(item.children)}
</li>
`.trim();
})}
</ul>
`.trim();
};
`${genList(items)}`;
</script>