Introduction
This article is all about what view encapsulation (Scoped style) is and how it is achieved in Angular.
Prerequisite
I am assuming that all readers have a prior knowledge of Angular 2 and above versions and what component is and how CSS is applied to a component.
Idea Of View Encapsulation
As we know in Angular, the CSS applied to one component is scoped to that component only and does not leak outside that template. But how does this work.
Before we reach there, we have to know the concept of Shadow DOM. ShadowDom
is basically a specification that enables DOM tree and style encapsulation. In simple words, we can say it allows us to apply scoped style to elements without bleeding out to the outer world. This is a new kind of feature of browsers and not all browsers at this time support this.
Let me show you shadowDOM in action.
var elm = document.querySelector('myfav');
elm.innerHtml = `<style>h1 {color : blue} </style>
<h1>Test</h1>`;
So you guys must be familiar with this plain JavaScript code. Here, we are just having an element reference of myfav
element (myfav
here could be any Angular component) with the help of querySelector
method. Now we have element reference so with the help of innerHtml
property, we are setting a color
of h1
to blue
.
But there is a problem with this approach. The problem here is that this style leaks outside this element. So if I have another h1
somewhere else, this style will be applied to that also. We don't want that. If we are making a component and have some styles to it, we would like to have those styles be scoped to that component only. Or for other case, let's say we want to use a component developed by some other folks and they might have defined some styles under that component and when we bring that component in our application, we don't want those styles to override the styles in our application and that's where shadow DOM shines.
We can change the above code and can use shadow dome with just an extra line of code like below:
var elm = document.querySelector('myfav');
var root = elm.createShadowRoot();
root.innerHtml = `<style>h1 {color : blue} </style>
<h1>Test</h1>`;
So here, after we get the reference of the element in elm
variable, we simply call createShadowRoot()
method and this gives us root of shadowDOM
for that element. Then, instead of using innerHtml
property of the element reference, we use innerHtml
property of shadowroot
and with this, the style of our h1
style will be scoped to this h1
element and will not leak outside of this element. This is shadowDOM
.
You might wonder what this has to do with Angular. So answer to that, as I have earlier mentioned, this shadowDOM
is not supported by all browsers as of now so to have this feature, Angular has used a trick of its own or we can say Angular has emulated the shadowDOM
.
Angular is achieving functionality of shadowDOM
through its concept called ViewEncapsulation
. This is an enum
which has three properties:
Let's see through the code how these values works.
Note: Here, I am assuming that you all know what component is and how component is created. Here I am using courses component which will render list of courses. You can use your own component and logic inside that, as while illustrating this example, our main focus is going to have on component metadata not on logic inside component.
Shown below is our simple courses component with its metadata.
Now to have the ViewEncapsulation
, we will use encapsulation
property of components
metadata and will assign ViewEncapsulation enum
values to it like below.
Note: For using viewencapsulation
property of metadata
, we have to import ViewEncapsulation
from @angular/core
.
When we hit the browser on localost:4200
, we will see the below page with a list of courses and by default, the first one is selected.
Now here, we will see how Angular has a functionality of scoped style or functionality of shadowDOM
. Ok, while inspecting an HTML in browser window of Angular application, you guys must have noticed _ngcontent[id]
on all the HTML elements and on your custom component. This is the only guy who is playing the major or we can say most of the role in achieving Angular a scoped style.
So hit browser console again and inspect the rendered HTML. In the <head>
section, there will be all styles listed used in this HTML. From all the listed styles, expand our style that we have used for our component in component's CSS file.
Here, you may be noticing [_ngcontent-c1]
attribute that is dynamically applied to the span.active
by Angular. So you may be guessing what this attribute is for.
So to answer that, you might be noticing above in our <app-courses>
element which is our host element for our courses component, inside our <app-courses>
element, there is a <div>
and inside that <div>
, there is a <span>
. You can see here that _ngcontent-c1
attribute is applied here as well which we were guessing what this attribute is for.
So Angular attaches an attribute to our element and uses that attribute to post process our CSS likes in the above shown example Angular attaches _ngcontent-c1
to the div
and the same is used in our CSS too. In this way, that CSS will be applied only to that element which will have that span.active
class as well as _ngcontent-c1
attribute. If you look anywhere in this document, you will not find _ngcontent-c1
attribute anywhere else. Also, we don't have to worry about how this attribute will be generated and how it will be applied. It's purely an Angular job to do.
In this way, Angular tries to emulate the concept of shadowDOM
in its framework. Also, this is the default behaviour of Angular means to have emulated view encapsulation, we don't have to use that encapsulation
property in our metadata.
Now, we will see how Native
property of ViewEncapsulation enum
works. As we have already mentioned, this will not work on all the browsers but to demonstrate, we will see how it works as on my browser.
Change the ViewEncapsulation enum
value to Native
in component metadata.
Now, on browser, hit the inspect button and open the browser console. Again, in head
section, we have three styles, but you may notice that there is no fourth style where we had our post process CSS rule.
Now if you scroll down, under <app-courses>
element, we have shadow-root
(marked in black above). Under the shadow-root
, we have style
element and span.active
class is applied here. So it does not have an additional attribute. All the style is scoped here. In this way, Native
property works.
Now, the third property of ViewEncapsulation
is None
. If we use this property in our component metadata, the style will be leaked to other element also.
History
- 26th December, 2019: Initial version