Should the actual implementation be written up, please leave a comment below if you're hitting the same set of issues.
Building my dev.to replica in Angular, the comments section presented a particularly awkward rendering challenge. Its structure is inherently recursive, branching downward without any imposed depth limit.
├── comment
│ ├── comment ─── comment
│ │ └── comment
│ ├── comment
│ ├── comment ─── comment ─── comment
│ │ └── comment ─── comment ─── comment
│ └── comment
├── comments
│ ├── comment ─── comment
│ │ └── comment
│ └── comment
├──── comment
│ ├── comment
│ └── comment
└──── comments
Rendering comments
The comments' depth is entirely unpredictable. After studying the data, it became obvious that the pattern mirrors recursion—the same component invokes itself. I applied that idea, and it functioned perfectly.
I handed the comments array to a component, which uses an ngFor to display it, then I call that same component, feeding it the children, and repeat the process indefinitely. For a demonstration, see the code.
Spacing comments
Styling posed the second hurdle: I needed to indent each comment as it descended a level, but beyond four levels, the indentation must cease, or the comments would run out of horizontal room. Unlike the previous issue, we already know the exact depth at which to stop applying the margin—so if we don't aim for a fully generic solution, we can hardcode it by simply placing a break
.app-comments .app-comments .app-comments .app-comments {
padding-left: 0;
}
By leveraging specificity, we can make use of the fact that we already know exactly which nesting level needs its padding removed.
Toggling functionality
Relying on an external library for state management in such a hierarchical structure can quickly become a headache. I opted for the native HTML details element, which provides built-in expand and collapse behavior, eliminating the need for custom state tracking—an eye-opener regarding the strength of these built-in primitives.
State problems
While the details element served its purpose, I still needed to access its open/closed state within the template to conditionally display content. My initial attempt involved template variables combined with @ViewChild, which turned out to be a complete failure. Managing state across an array of dynamic and ever-changing length proves exceptionally difficult. Rather than maintaining a mirrored tree-like state for each component, I decided to keep everything within the template by referencing the same variable and directly utilizing the native open attribute to control visibility. Yet, no changes were reflected—despite my best efforts, the open property remained static in the template.
Ngzone problem
It turned out that the details element emits a toggle event, but unlike many other events, zone.js does not patch it. It’s frustrating when a framework fails to behave as expected, requiring deep internal knowledge to resolve—something nearly impossible without extensive experience. The solution involved explicitly importing ChangeDetectorRef and invoking detectChanges upon each toggle event firing.
