When and why should we use View Encapsulation in angular - css

When working in angular is there a particular rule or guideline that should be used in deciding when to use and why to use View Encapsulation?
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.None /*What is the use of this line*/
})
Can someone please explain in simple ways?

Codecraft.TV has an amazing article on ViewEncapsulation that you can refer to get a better understanding.
To just summarize it:
ViewEncapsulation.Emulated: Angular changes our generic css class selector to one that target just a single component type by using automatically generated attributes.
Any styles we define on a component don’t leak out to the rest of the application but with ViewEncapsulation.Emulated our component still inherits global styles.
ViewEncapsulation.Native: If we want Angular to use the shadow DOM we can set the encapsulation parameter to use ViewEncapsulation.Native
With ViewEncapsulation.Native styles we set on a component do not leak outside of the components scope.
This is great if we are defining a 3rd party component which we want people to use in isolation. We can describe the look for our component using css styles without any fear that our styles are going to leak out and affect the rest of the application.
However with ViewEncapsulation.Native our component is also isolated from the global styles we’ve defined for our application. So we don’t inherit the global styles and have to define all the required styles on our component decorator.
Finally ViewEncapsulation.Native requires a feature called the shadow DOM which is not supported by all browsers.
ViewEncapsulation.None: If we don’t want to have any encapsulation at all, we can use ViewEncapsulation.None.
If we don't encapsulate anything, the style we defined in the component will leak out and started affecting the other components.
Some other resources that you might want to have a look into:
VIEW ENCAPSULATION IN ANGULAR - By Thoughtram
View Encapsulation by Rangle.IO
Scoping Your Styles in Angular With ViewEncapsulationView
Diff between ViewEncapsulation.Native, ViewEncapsulation.None and ViewEncapsulation.Emulated

Let's take one example where we have one parent and one child component with their own html, ts, css files.
Suppose in parent component's html you referenced child component like below-
parent.component.html -> <app-child> </app-child>
Now, if you create and add any similar styles like parent.component.css file in child.component.css (let's take <p> tag as an example) then those will get added to each component seperately even if all angular component's html gets rendered on single page.
So now you will have seperate styles for <p> in child component.(Behind the scene angular adds one random_atttr to each component and then to all elements inside that component)
This behaviour of angular, comes in view encapsulation. Used by angular like shadow DOM techonlogy which is not supported by all broswers but angular does it like this.
ViewEncapsulation has 3 options -
encapsulation: ViewEncapsulation.None,
ViewEncapsulation.Emulated, (-- this is default)
ViewEncapsulation.Native (-- only applies to browsers with shadow DOM technology)<br>

Related

Angular: Component styles do not cascade to sub-components?

In my app.component.html I have the following, working class assignment.
<body>
<span class="test">SHABOO</span>
<div class="container">
<router-outlet></router-outlet>
</div>
</body>
In the outlet, a component is rendered and it contains the following.
<span class="test">HAZAA</span>
I expected it to get the same style as the first one but it seems that the style doesn't cascade down to the component. It made me uncertain if I'm mistaken about how the styles are supposed to behave between parent and child components in Angular.
I made sure that the name of the class isn't overriden (to exclude the risk of collision). At the moment, I'm putting similar chunks of SCSS code in each component and that's obviously bad practice.
If I #import "../app.component.scss", the styles kicks in. But I was under the impression that even without the import, the style is supposed to cascade.
Angular components use view encapsulation. This is by design so that components are reusable across applications. To treat a component's styles as global, set the view encapsulation mode to none (not recommended).
Instead of using none, register global style files in the Angular CLI styles section, which is pre-configured with the styles.css file.
Component CSS styles are encapsulated into the component's view and don't affect the rest of the application.
To control how this encapsulation happens on a per component basis, you can set the view encapsulation mode in the component metadata. Choose from the following modes:
ShadowDom view encapsulation uses the browser's native shadow DOM implementation (see Shadow DOM on the MDN site) to attach a shadow DOM to the component's host element, and then puts the component view inside that shadow DOM. The component's styles are included within the shadow DOM.
Native view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - learn about the changes.
Emulated view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing (and renaming) the CSS code to effectively scope the CSS to the component's view. For details, see Appendix 1.
None means that Angular does no view encapsulation. Angular adds the CSS to the global styles. The scoping rules, isolations, and protections discussed earlier don't apply. This is essentially the same as pasting the component's styles into the HTML.
To set the components encapsulation mode, use the encapsulation property in the component metadata:
src/app/quest-summary.component.ts
// warning: few browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.Native
ShadowDom view encapsulation only works on browsers that have native support for shadow DOM (see Shadow DOM v1 on the Can I use site). The support is still limited, which is why Emulated view encapsulation is the default mode and recommended in most cases.
External and global style files
When building with the CLI, you must configure the angular.json to include all external assets, including external style files.
Register global style files in the styles section which, by default, is pre-configured with the global styles.css file.
See the CLI wiki to learn more.
This is how angular works. Every component is separated with it's CSS, even if it is child component of a parent. They can't inherit their parent CSS.
Haza span component is child of your app component, but it is separated component. So, you can't get those Shaboo styling to Haza.
Solution:
if you want to maintain same CSS, there are multiple ways.
Recommended way:
a) if it is only for onle class, copy the class to the child component.
b) if it is for huge number of classes, create a css file for them and import them in both parent and child component css.
Example:
#import '../span.css';
you can add this css to styles.css. then you will get it anywhere in the app. if you want this style many places, you can do it.
As #Christopher Peisert mentioned, by using
encapsulation: ViewEncapsulation.Native
end of the #component decorator in child.component.ts. It is not recommended way
The global CSS files are those that are listed in angular.json. All others CSS files are encapsulated by default (this can be removed but is not recommended.)
Keeping your global CSS organised is important as your application grows. What I like to do is create a global styles folder and then add new files for different widgets that I might be altering. Since removal of ::ng-deep these types of changes have to be done in the global scope.
It works like that. If you are in need of having a common style then add it in the global styles.scss file.
If you want a global style put it in the global file styles.css or styles.[Your css preprocessor]
See this example https://stackblitz.com/edit/angular-a5ntm6?file=src%2Fstyles.css
I put test class at the end
.test{
color:red;
}

is it possible to write CSS for all Angular "hosts" or do we have to select each host individually

The Angular documentation for :host does not mention that we can style all host elements at once. I have added this CSS at a “global” level:
:host {
width: 100%;
}
but there was no effect. However, this works fine in the CSS at the component level:
:host(app-search) {
width: 100%;
}
where app-search is a component, app-search.component.ts.
Is is possible to write a :host selector for all components or must this be declared multiple times at the component level?
By its very definition (or specification), :host can never be used at a global level. It is created to use from component level and to select the parent component (which is called shadow host) from the children (which is called shadow tree).
For more clarification, Angular's :host selector is a special selectors from the world of shadow DOM style scoping (described in the CSS Scoping Module Level 1 page on the W3C site).
The angular documentation clearly specifies that you should use this selector to select the parent component from within the child component. But it may seem unclear to you if you don't have any idea how shadow tree works. see the documentation.
If you need to style any component from a global stylesheet, there is a style.css file automatically added at a global scope in Angular. Just put your CSS in that file and you can find it available globally in all components.
Component level CSS files make your CSS modular. This is a great feature because:
You can use the CSS class names and selectors that make the most
sense in the context of each component.
Class names and selectors are local to the component and don't collide with classes and selectors
used elsewhere in the application.
Changes to styles elsewhere in the
application don't affect the component's styles.
You can co-locate
the CSS code of each component with the TypeScript and HTML code of
the component, which leads to a neat and tidy project structure.
You
can change or remove component CSS code without searching through the
whole application to find where else the code is used.
Although it is configurable, I strongly recommend not to use ViewEncapsulation.None. It will make kill all your CSS modularity which you can avail easily using global CSS files without affecting the scoping restriction.
In your app-search.component.ts file, you could set:
encapsulation: ViewEncapsulation.None
like this
#Component({
templateUrl: 'app-search.component.html',
styleUrls: ['app-search.component.css'],
encapsulation: ViewEncapsulation.None
})
This prevents you from having to rewrite styles and enables styles on a global level.
You could also try applying the styles directly into the index.html file. These styles will also be global, preventing you from rewriting styles at the component level.

CSS classes not picked up during Angular 5 compilation

I'm using ag-grid in my angular 5 project. I'm trying to add css classes to the cells. Having looked the extensive documentation on ag-grid cell style I have tried to use cellClass and cellClassRules.
The styles are defined in the scss file, as an example:
.readonly-cell{ background-color: #cccccc; }
The scss file is then included in the component:
#Component({
selector: 'app-volume',
templateUrl: './volume.component.html',
styleUrls: ['./volume.component.scss']
})
I then apply the class to the column:
{headerName:'Power', field:'power', width:150, cellClass:'readonly-cell'}
The grid itself is working fine. The issue is that the the power cells do not change colour. When I check the rendered HTML in Firefox, I can see that the cell have indeed have the class readonly-cell applied to them. But the style details are not listed on the rules panel.
This makes me think that the classes are not being picked up during the compilation. I don't think it's an issue with ag-grid, but the way the style classes are being picked up.
Is there any way to troubleshoot why the classes and the definitions are not being included in the compilation?
You get that behavior because element you are trying to target by a CSS rule is generated outside your Angular component and Angular adds special attributes so that component CSS applies only to that component (and not to its child components, any DOM nodes added outside Angular etc.). You should either build all the HTML you need to style using Angular and in exactly the same component as your styles, or disable that feature. You can disable it either by using ViewEncapsulation.None:
#Component({
selector: 'app-volume',
templateUrl: './volume.component.html',
styleUrls: ['./volume.component.scss'],
encapsulation: ViewEncapsulation.None
})
or by using /deep/ in your stylesheet, as described here:
https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep

Shared styles across components in an Angular 2 app

I've got some CSS rules in my Angular 2 app that would be common across various components. Obviously I don't want to copy&paste them into each component's styles. I currently have 2 ideas:
Place common CSS rules in a static CSS file and include it using a link in my index.html's head section.
Place my common CSS rules in one or more files and include them in #Component decorator for each component, e.g.
styleUrls: [ './myComponentStyle.css', '../common/common.css']
First approach look not-so-angular-ish to me, but at the same it's sure to work and simple to implement.
Second one requires some work to be done with each component, but allows more control about what styles are being used by one. It also lets me to organize my common styles into smaller stylesheets and use only ones that are needed.
Do you favor any of those solutions or is there a third, better one? :)
1. This solutions is good, but it's more suitable for any common styles, which should be available for all components. For example, styles for css grids.
To make it more angularish you could set encapsulation for you app component to none:
`#Component({
selector: 'my-app',
template: ` `,
styleUrls: ["shared.style.css"],
encapsulation: ViewEncapsulation.None
}) export class App {}`
Demo could be found here (plunker)
Note: Styles, included by this ways (just adding style tag, or with non encapsulation) will affect all elements on your pages. Sometimes it is want we really want (agreement to use any css framework for hole project). But if just want to share styles between few component - it would be probably not the best way.
Summary:
(+) easy to use
(-) no encapsulation
2. I like this solution, because it is very understandable and has predictable behavior. But there is one problem with it:
It will add style tag with your shared styles every time you use it.
It could be a problem if you have big style file, or many element which are using it.
#Component({
selector: 'first',
template: `<h2> <ng-content> </ng-content> </h2>`,
styleUrls: ["shared.style.css"]
})
export class FirstComponent {}
Demo could be found here (plunker)
Summary:
(+) easy to use
(+) encapsulation
(-) duplicates styles for every usage
3. There is one more option you could use.
Just create one more component which will provide shared styles for it's children.
` <styles-container>
<first> first comp </first>
</styles-container>
<styles-container>
<second> second comp </second>
</styles-container>`
In those case you will have to use /deep/ in your styles to make style available for child components:
:host /deep/ h2 {
color: red;
}
I also worth to be mentioned not to forget use :host to make styles available only for child elements. If you omit it you will get one more global style.
Demo could be found here (plunker)
Summary:
(-) you have to create container and it in templates
(+) encapsulation
(+) no duplicated styles
Notes: Encapsulation of styles is really cool feature. But you also should remember that there no way to limit your deep styles. So if you applied deep styles, it would available absolutely to all children, so use it careful too.
There are 3 ways to use styling in angular2 app (link).
You have mentioned two of those that allows you to reuse styles.
My personal opinion is that for any large application its preferable to go with #2 mainly due to the view encapsulation provided by angular.
#1 can be used for the really very generic styles that are common to all parts of your application. But if you will take into account that the root in your SPA will be angular component anyway - there is no real need to go with another approach of linking styles than #2.
Moreover by working with css in two different ways you will have to remember this (and handle with some extra code) when for example bundling your app and using tools like gulp-inline-ng2-template
For future readers, I think this solution is best.
Let's assume you have 2 components(products and customers), and have common styles to be shared.
1.Create one more component
//customer-products-styles.component.ts
#Component({
selector: "app-customer-products-styles",
template: "",
styleUrls: ["./customer-products-styles.component.scss"],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CustomerProductsStylesComponent {}
//customer-products-styles.component.scss
app-products,
app-customers {
p {
color: coral;
}
}
2.Use it like so
<!-- Customers Component (app-customers) -->
<app-customer-products-styles></app-customer-products-styles>
<p>
customers works!
</p>
<!-- Products Component (app-products) -->
<app-customer-products-styles></app-customer-products-styles>
<p>
products works!
</p>
Benefits
It is lazy-loaded, loads when module chunk is downloaded, initial
main.js is reduced
Adding component selectors (app-customers and
app-products) as a parent for styles make it component scope
No duplicate styles and only load once in the browser, whatever the component request it first
Some Additional points
Set encapsulation to none, but add component selector as parent in the style
I also changed default changeDetection to OnPush, there is no need but to be safe
Working Stackblitz

Pseudo elements in Angular5

Why are regular pseudo-classes from CSS in Angular5 not working properly? I know that you can mimmic their behaviour using the Angular event handlers like (click)="function1()" or (mouseenter)="function2()".
What is the reason we can't use regular CSS pseudo-classes in Angular?
CSS is working just fine with Angular.
That said, Angular encapsulates the CSS for the current component you're working on (without you having to worry about that). It's a feature that you get with shadow DOM but as not all the browsers supports that, Angular is emulating it by adding properties on your components like that:
<div _ngcontent-c6>... your content ...</div>
And the original CSS you wrote is then changed by angular to:
div[_ngcontent-c0] {
color: red; // for example
}
So if you want to write some CSS that will affect other components and not just the current one, you should set the view encapsulation to none:
#Component({
selector: '...',
templateUrl: '',
styleUrls: [''],
encapsulation: ViewEncapsulation.none
})
Or, if you have an angular cli project, you can also define the global styles in: styles.scss (or .css) file which doesn't have any view encapsulation.
See more here: https://angular.io/guide/component-styles#view-encapsulation
(But before turning that feature off, think twice about it as it's really useful)

Resources