Angular Elements content projection should require Shadow DOM
- Dominant language
- TypeScript
- Stars
- 101k
- Forks
- 27.5k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 288
Description
### Which @angular/* package(s) are the source of the bug?
elements
### Is this a regression?
No
### Description
Angular Elements currently supports "content projection" from the initial render during component upgrade when children are already present.
My main issue with it is that neither the DOM nor custom elements have any meaningful semantics for what "content projection" means in a way compatible with Angular. You can always create a DOM element and then add children to it, but if that element is creating children of its own, there is no distinction between its children and your "content projected" children. This conceptual flaw manifests in a few ways.
First, Angular only performs content projection on component upgrade (typically when attached to the document). If you later add children to the element, this does not get projected into an `` slot and just gets appended to the host element.
Second, content projection may _move_ elements, meaning what originally looks like:
```html
```
Post-upgrade might suddenly look like:
```html
```
The component's internal template confuses the expected contract of content projection. `myComp.firstElementChild` no longer refers to `#hello`, and instead refers to `#goodbye`. This is relatively understandable in the context of a light DOM tree, but it means that we've lost the distinction between the projected content and the internal component content. This is necessary to _change_ the projected content, which is no longer easily possible. Even if you keep a reference to the projected element and replace it in the DOM, Angular retains a reference to the old element and will continue to project it.
This movement also makes it impossible to recreate the underlying Angular component because moving the projected children means we can no longer find them as direct children of the custom element, the process is not repeatable. This is problematic for [managing the component lifecycle](https://github.com/angular/angular/issues/64229) as indicated [here](https://github.com/angular/angular/issues/38778#issuecomment-690433315).
It is my belief that content projection as Angular understands it is fundamentally incompatible with custom elements and the way the DOM works. I think the only sane behavior here is to remove the feature altogether and require Angular Elements to be empty during the initial render, allowing Angular full control over their contents.
I think the one subcase which can be made to work is if the underlying Angular component uses `ViewEncapsulation.ShadowDom`. This solves most of the conceptual issues as the light/shadow distinction disambiguates between elements provided in content projection and elements owned by the component template directly. Consumers can mutate element children at any time and see those reflected in the expected way. This likely requires some non-trivial changes to Angular internals to make content projection fit the `` model, but at least in theory I believe this does "square the circle" with respect to content projection in custom elements.
### Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/stackblitz-starters-9edtpmzx?description=An%20angular-cli%20project%20based%20on%20@angular/animations,%20@angular/common,%20@angular/compiler,%20@angular/core,%20@angular/forms,%20@angular/platform-browser,%20@angular/platform-browser-dynamic,%20@angular/router,%20core-js,%20rxjs,%20tslib%20and%20zone.js&file=src%2Fmain.ts,src%2Findex.html&template=node&title=Angular%20Starter
### Please provide the exception or error you saw
```true
```
### Please provide the environment you discovered this bug in (run `ng version`)
```true
v20
```
### Anything else?
_No response_
Contributor guide
Research direction
Start with the @angular/elements package and reproduce the behavior using the linked StackBlitz example, focusing on initial content projection, later child insertion, and component recreation. The issue names no source files or tests; completion would require an agreed design and corresponding regression coverage for Shadow DOM or empty-initial-render behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- angular, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100