03 Component Scoped Styles - juanfevasquez/Ng-BasicStyling GitHub Wiki
On branch: demo/component-scoped-styles
We learned how to work with global styles. Remember that when adding all of your app styles into your main style.css file, you're allowing everything to be global.
Here we are going to change that adding some properties and files to our app and card files.
In our app.component.ts file, inside our @Component decorator we will add the following property:
styleUrls: ['./app.component.css']
What we're telling Angular is: hey, I want to add my css files for this piece of the app in this new file called app.component.css. Hence you must create a new css file under app folder with the mentioned name.
There we will paste part of our css code that was inside of style.css
IMAGE PENDING
For our card we are doing the same. We go into our card folder and create a new css file called card.component.css and cut/paste our card styles from styles.css. Inside of card.component.ts we add the new property to the decorator:
styleUrls: ['./card.component.css']
Now run ng serve and let's see what Angular-cli/Webpack injects for us
You should see something like this:
IMAGE PENDING
We have 3 style tags injected in our head. The first one belongs to styles.css. The second belongs to app and the third are the styles from our card component.
But what are these?
IMAGE PENDING
There's a very important concept that I briefly explained in the Readme.md file and that's Shadow DOM. Shadow DOM is a feature that comes with Web Components, where you're allowed to encapsulate all of our html, css and javascript inside a Custom Element. This way, the css we add to our Custom Element work only there and can't alter the styles from other custom elements or any global styles. Remember to visit my Shadow DOM repo if you want to dig a little bit deeper into the subject.
But Shadow DOM is not yet supported by all browser and we wish we could use it in our Angular App.
Well, it happens that we can use it, in different ways. Angular provides us lots of flexibility here by using a module called View Encapsulation. View Encapsulation allows us to use native Shadow DOM, emulate something that kind of works like Shadow DOM or not using anything.
Let me explain by doing.
We will start with the default option which is Emulated Shadow DOM. Go to your card.compoent.ts and in the decorator, well... you don't have to do anything since this is the default behavior!
Ok ok, let's write it anyway, redundancy is welcome here. Let's add this new property to our card decorator:
encapsulation: ViewEncapsulation.Emulated
Every time you use styleUrls, or inline styles in your template, ViewEncapsulation.Emulation is kicked into action. This is how Angular simulates Shadow DOM, though it is not as powerful as the real Shadow DOM. The objective of emulating is encapsulating the css you write for the component, avoiding conflict with the global css or any css from another component. Angular then injects attribute selectors to your css and DOM since it is a browser friendly option
IMAGE PENDING (show attributes injected into DOM and css generated with the selectors)
By the way, when you add the encapsulation property to the decorator, make sure you are importing the ViewEncapsulation module from @angular/core.
"I hate attribute selectors, they look nasty and they try to fool us into believing it is the same as Shadow DOM". Sure, it is not the perfect solution. It is a good alternative though, and it is browser friendly. But what if you don't want to have these attribute selectors added to your css? Then you'd have to deactivate it by doing the following. Just change .Emulated for .None, like this:
encapsulation: ViewEncapsulation.None
This will make the attribute selectors disappear from your css and html, it will also stop protecting you from overriding your component styles since you're exposing them globally, so any other style from a styles.css or other component without ViewEncapsulation can generate conflict.
Now, continuing with our card component, change the following:
encapsulation: ViewEncapsulation.Native
This will enable the use of Shadow DOM but this can only be used with some browsers (Chrome of course).
To enable support and to inspect shadow DOM elements :
- Enable the Shadow DOM and Developer Tools experiments in chrome://flags
- Enable the Shadow DOM in the Inspector settings, this panel can be accessed by the cog on the bottom right of the inspector.
- Check the 'show shadow DOM' checkbox.
- Restart your browser
We can add our styles in the same file as our Component.
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styles: [`
.card {
width: 100%;
max-width: 300px;
padding: 5px;
margin: 0 5px 15px;
border: 1px solid #ccc;
background-color: white;
},
.card-thumbnail {...}
`],
})
This will generate the same result as using a styleUrls that link to card.component.css.
We can also embed our styles in our template. The end result is the same, a style injected into the head.
@Component({
selector: 'app-card',
template: `
<style>
.card {
width: 100%;
max-width: 300px;
padding: 5px;
margin: 0 5px 15px;
border: 1px solid #ccc;
background-color: white;
},
.card-thumbnail {...}
</style>
<div class="card">...</card>
`
})