- Angular Elements, Part I: A Dynamic Dashboard In Four Steps With Web Components
- Angular Elements, Part II: Lazy And External Web Components
- Angular Elements, Part III: Angular Elements without Zone.js
- Angular Elements, Part IV: Content Projection with Slots in Angular Elements (>=7)
- Angular Elements, Part V: Your Options For Building Angular Elements With The CLI
Our gratitude goes to Rob Wormald from the Angular Team, the creator of Angular Elements, for his review of this article.
Starting with Angular 7, developers have the ability to employ slots for projecting markup into a component's template. This feature is rooted in Shadow DOM v1, which Angular has supported since version 6.1.
This guide demonstrates the usage of both Shadow DOM v1 and slots. The example component renders a diagram:

The title, description, and legend positioned at the bottom can be customized through content projection with slots. The complete code is available in the GitHub repository (on the ng7 branch):
If this topic piques your interest, you might also look into our Angular Workshop focused on Enterprise Architectures.
Shadow DOM v1
Shadow DOM has long been a core concept for Angular Components. By default, Angular emulates this standard to keep a component's styles separated from those of other components. The goal is to prevent one component's local styles from affecting others.
This encapsulation can also be disabled or configured to use the browser's native implementation, which offers a stronger layer of isolation compared to the emulated version.
Until Angular 6.1, the framework backed what is called Shadow DOM v0. Since then, browser vendors have aligned on the revised Version 1, which is being adopted widely. At the time of this writing, Chrome, Safari, Firefox, and Opera already had support, and the Edge team was in the process of integrating it.
To leverage Shadow DOM v1 in Angular (>= 6.1), you simply set the encapsulation property in the Component decorator to the newer ShadowDom value:
@Component({
selector: 'app-dashboard-tile',
templateUrl: './dashboard-tile.component.html',
encapsulation: ViewEncapsulation.ShadowDom
})
export class DashboardTileComponent implements OnInit {
@Input() a: number;
@Input() b: number;
@Input() c: number;
[…]
}
Be careful not to mix up ViewEncapsulation.ShadowDom with ViewEncapsulation.Native, which has been around since Angular's inception and uses Shadow DOM v0.
Using Slots for Content Projection
A considerable number of Web Components — or more accurately, Custom Elements — need to be flexible enough to accept markup as their content. This action is referred to as content projection, where the provided content is mapped to specific positions within the element's template.
Shadow DOM v1 addresses this with the introduction of the slot element. Each slot designates a location within a template designated for receiving projected content:
<div class="card">
<div class="header">
<h1 class="title"><slot name="title">Standardwert</slot></h1>
</div>
<div class="content">
<div style="height:200px">
<ngx-charts-pie-chart [labels]="true" [results]="data">
</ngx-charts-pie-chart>
</div>
<p><slot>Standardwert</slot></p>
<i><slot name="legend">Standardwert</slot></i>
</div>
</div>
Between the opening and closing slot tags, you can include fallback markup that displays when the caller supplies no content for that particular spot. There is the possibility of having one default slot and several named ones.
When invoking a Custom Element, the slot attribute is used to designate which named slot within the component's template should receive the content:
<div class="col-sm-3">
<dashboard-tile a="10" b="5" c="15">
<span slot="title">Important Stuff</span>
<span slot="legend">A, B and C show the values of A, B and C.</span>
Only believe in statistics you've faked yourself.
</dashboard-tile>
</div>
Any content assigned to a specific slot will be projected there. All remaining content is placed into the default slot.
Accessing Projected Content
Angular provides several hooks for accessing projected content, including ngAfterContentChecked and ngAfterContentInit, as well as content queries like @ViewChild and @ViewChildren.
These constructs, however, do not function with slots. An alternative is to utilize the slotschange event:
<i><slot (slotchange)="slotChange($event)" name="legend">No Legend available.</slot></i>
Within the event handler we establish, we can retrieve the projected elements:
@Component({
[...]
encapsulation: ViewEncapsulation.ShadowDom
})
export class DashboardTileComponent implements OnInit {
slotChange($event) {
const assigned = $event.target.assignedNodes();
if (assigned.length > 0) {
console.debug('shotchange', assigned[0]);
}
}
}
