Welcome to today’s post.
In today’s post I will explain what View Encapsulation is and how to use it within an Angular application.
You may have seen of heard of what View Encapsulation is and examples of its use but weren’t quite sure how it affected the rendering of your Angular component or when it to make use of it. In this post I will explain what View Encapsulation is and examples of how it is used.
What is View Encapsulation?
View encapsulation is a feature within Angular applications that allows the styles within components to be localised within the scope of the component.
Why is this a useful feature? Encapsulation of styles is useful within applications for the following reasons:
- Whenever we wish to add styles to a component without affecting other application components.
- Experimenting with styles in a localised part of the application before applying it to other components or globally to the rest if the application.
- Minimizing impact of changes to the global stylesheet, styles.css from multiple concurrent developers.
- Promoting changes to the global stylesheet when encapsulated styles across multiple components are thoroughly tested.
How is View Encapsulation used?
To use View Encapsulation within a component we use the encapsulation property. There are three modes that are used:
- ShadowDom
- Emulated
- None
The ShadowDom property allows the component that uses shadow DOM to isolate its CSS styles to a DOM tree within the component’s HTML template without impacting styles outside of the component. However, the browser support is limited.
The Emulated property, which is the default encapsulation mode, converts the component’s CSS to the view within the component’s HTML template.
The None property allows CSS styles within a component to be visible as a global application style. Essentially, this is the same effect as defining styles within the global styles.css and using them within a component.
I will now provide an example of how to apply view encapsulation and to understand how it affects styling across components within an application.
Below are the definitions of three components, a blue component, a red component, and the parent component:
Blue Component
HTML
<p style="paragraph-color">my-blue-component!</p>
CSS
p, .paragraph-color {
color: skyblue;
background-color: blue;
}
TS
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-my-blue-component',
templateUrl: './my-blue-component.component.html',
styleUrls: ['./my-blue-component.component.css'],
encapsulation: ViewEncapsulation.Emulated,
})
export class MyBlueComponentComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
Red Component
HTML
<p style="paragraph-color">my-red-component!</p>
CSS
p, .paragraph-color {
color: skyblue;
background-color: red;
}
TS
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-my-red-component',
templateUrl: './my-red-component.component.html',
styleUrls: ['./my-red-component.component.css'],
encapsulation: ViewEncapsulation.Emulated
})
export class MyRedComponentComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
Parent Component
HTML
<hello name="{{ name }}"></hello>
<p>A demo of view encapsulation :)</p>
<app-my-blue-component></app-my-blue-component>
<app-my-red-component></app-my-red-component>
CSS
p {
font-family: Lato;
}
Emulated Encapsulation
With both components using emulated encapsulation, the resulting styles are localized to each component, and the application component is unaffected. This is shown in the background colors of each component when the application landing page shows:
On inspection of the styles in the above rendered screen, notice that each component with emulated encapsulation view has an additional attribute added that is prefixed by _nghost:
The prefixing of the _nghost attribute ensures the CSS styles are unaffected by CSS styles within other un-encapsulated components.
Components with No Encapsulation
I will now modify the encapsulation for the red component to using no encapsulation as shown:
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-my-red-component',
templateUrl: './my-red-component.component.html',
styleUrls: ['./my-red-component.component.css'],
encapsulation: ViewEncapsulation.None,
})
export class MyRedComponentComponent implements OnInit {
constructor() {}
ngOnInit() {}
When the application is rebuilt and refreshes, the main screen is slightly different:
Notice that the CSS style from the red component, which uses no encapsulation have bled out to other components within the application that do not use their own styles. In this case, the paragraph CSS style for the AppComponent has inherited the styles from the red component!
Again, we inspect the styles within the rendered HTML and notice that the red component no longer has the generated _nghost attribute.
If we amend the CSS for the AppComponent as shown:
p {
font-family: Lato;
color: black;
}
On rebuild and refreshing the application, the main screen loads as shown:
In the above case, CSS styles (the paragraph background) from a component with an un-encapsulated view have merged with styles (the paragraph font) from another component, the AppComponent, which has an un-encapsulated view.
When the encapsulation mode is omitted from a component, the default mode used is Emulated, so in the above inspected HTML, we saw the _nghost attribute added to the component’s HTML attributes.
Shadow DOM Encapsulation
With a Shadow DOM encapsulation, the styles stay confined to the component. To demonstrate this we apply the following changes to the AppComponent:
HTML
<hello name="{{ name }}"></hello>
<p style="paragraph-color">A demo of view encapsulation :)</p>
<app-my-blue-component></app-my-blue-component>
<app-my-red-component></app-my-red-component>
TS
import { Component, VERSION, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.ShadowDom,
})
export class AppComponent {
name = 'Andrew!';
}
CSS
p, .paragraph-color {
font-family: Lato;
color: black;
background-color: green;
}
When the application rebuilds and refreshes, the rendering in the landing page resembles:
On inspection of the rendered HTML and styles we can see the Shadow DOM for AppComponent has a node #shadow-root as shown:
On expanding the <style> element of the red component, notice the styles are converted to attributes postfixed with _ngContent* attributes.
So, the CSS class attributes:
p, .paragraph-color {
…
}
are rendered with unique _ngContent attributes as shown:
p[_ngcontent-sha-c89], .paragraph-color[_ngcontent-sha-c89] {
…
}
This is shown in the inspected HTML:
I now change the encapsulation for the red component to:
encapsulation: ViewEncapsulation.None,
Then after rebuilding and refreshing, the rendering for the AppComponent with Shadow DOM encapsulation will have its CSS styles overridden from the red component:
The HTML inspection of rendered content shows the redefined style for the red component interferes with the style within the shadow DOM:
The above discussion and examples have shown what View Encapsulation is and what each of the three modes of view encapsulation are and how they affect component styles and the styles of other components within an application.
If you are aiming to support applications that will work in all browsers, then it is safer to use the Emulated encapsulation mode a this ensures the styles are encapsulated within their own component.
More details on View Encapsulation are on the Angular developer site.
That is all for today’s post.
I hope you have found this post useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.