Although it may sound clichéd, Angular 2 was built for the browsers of tomorrow. You can point to why this is the case in a large number of ways, but there is one way where this is extremely true: component encapsulation.
The ideal component model for Angular 2 is the one in which components are entirely sandboxed, save for the few pieces that are externally visible and modifiable. In this respect, it does a bang-up job, but even the most modern browsers limit its ability to strive for such efficacy. This is especially true in the realm of CSS styling.
Several features of Angular's component styling are especially important:
There are a number of interesting ways to accomplish an efficient component styling schema.
The code, links, and a live example related to this recipe are available at .
Begin with a simple application component:
[src/app/app.component.ts] import {Component} from '@angular/core'; @Component({ selector: 'app-root', template: ` <h1> {{title}} </h1> `, styles: [` h1 { color: red; } `] }) export class AppComponent { title = 'app works!'; }
Angular will default to not using ShadowDOM for components, as the majority of browsers do not support it to a satisfactory level. The next best thing, which it will default to, is to emulate this styling encapsulation by nesting your selectors. The preceding component is effectively the equivalent of the following:
[src/app/app.component.ts] import {Component, ViewEncapsulation} from '@angular/core'; @Component({ selector: 'app-root', template: ` <h1> {{title}} </h1> `, styles: [` h1 { color: red; } `], encapsulation: ViewEncapsulation.Emulated }) export class AppComponent { title = 'app works!'; }
How exactly does Angular perform this emulation? Look at the rendered application to find out. Your component will appear something like the following:
<app-root _nghost-mqf-1=""> <h1 _ngcontent-mqf-1=""> app works! </h1> </app-root>
In the head of the document:
<style> h1[_ngcontent-mqf-1] { color: red; } </style>
The picture should be a bit clearer now. Angular is assigning this component class (not an instance) a unique ID, and the styles defined for the global document will only be applicable to tags that have the matching attribute.
If this encapsulation isn't necessary, you are free to use encapsulation: ViewEncapsulation.None
. Angular will happily skip the unique ID assignment step for you, giving you a vanilla:
<app-root> <h1> app works! </h1> </app-root>
In the head of the document:
<style> h1 { color: red; } </style>
The best, most futuristic, and least supported method of going about this is to use ShadowDOM instances to go along with each component instance. This can be accomplished using encapsulation: ViewEncapsulation.Native
. Now, your component will render:
<app-root> #shadow-root <style> h1 { color: red; } </style> <h1> app works! </h1> </app-root>
Angular is smart enough to recognize where it needs to put your styles and how to modify them to make them work for your component configuration:
None
and Emulated
, styles go into the document headNative
, styles go inline with the rendered componentNone
and Native
, no style modifications are neededEmulated
, styles are restricted by attribute selectorsAn important consideration of ViewEncapsulation
choices is CSS performance. It is well known and entirely intuitive that CSS styling is more efficient when it has to traverse a smaller part of the DOM and has to match using a simpler selector.
Emulating component encapsulation adds an attribute selector to each and every style that is defined for that component. At scale, it isn't hard to see how this can degrade performance. ShadowDOM elegantly solves this problem by offering unmodified styles inside a restricted piece of DOM. Its styles cannot escape but can be applied downward to other components. Furthermore, ShadowDOM components can be nested and strategically applied.