Cannot Understand the Use of :host in components in Angular2 - css

What i have understood of host is that if i have a child component inside a parent component and we want to style a child component from the parent component we can use :host .
and :host-context for vice-versa.
Please let me know if this is the right use of host .
https://angular.io/docs/ts/latest/guide/component-styles.html
When i try to do the same in my App it dosent work
App component template
<div class ="top">
<h1>
Home Component
</h1>
<hr>
<app-ngrx></app-ngrx>
<router-outlet></router-outlet>
<div>
ngrx component template
<h3 class="mine">NGRX</h3>
<button (click)="increment()">Increment</button>
<div>Current Count: {{ counter | async }}</div>
<button (click)="decrement()">Decrement</button>
<button (click)="reset()">Reset Counter</button>
App component CSS
:host(.mine){
color:red;
}
This do not Seem to work Please help i am not able to understand.
I looked at this question But just not able to figure out
Angular 2: How to style host element of the component?
Updated after #Gunter Answer
In my app-ngrx template i have added
<h3 class = "mine">NGRX</h3>
<button (click)="increment()">Increment</button>
<div>Current Count: {{ counter | async }}</div>
<button (click)="decrement()">Decrement</button>
<button (click)="reset()">Reset Counter</button>
and in the app-ngrx css file i have added
:host(.mine){
color:red;
}
But even without adding mine in app component like
<app-ngrx></app-ngrx>
The h3 is red where as i feel it should be red when <app-ngrx class = "mine"></app-ngrx>

What i have understood of host is that if i have a child component
inside a parent component and we want to style a child component from
the parent component we can use :host . and :host-context for
vice-versa
No, this is not what it used for.
:host selector comes from shadow DOM spec.
...This scoped subtree is called a shadow tree. The element it's
attached to is its shadow host.
In angular world, a component's template is a shadow tree. The component's element is a shadow host. So when you're defining styles for :host selector, the styles are applied to the component's element.
:host
In your example, if you defined styles in my-app component, the styles will be applied to <my-app> DOM element. This particular configuration:
:host(.mine){
color:red;
}
Will be applied to the host element that has .mine class:
<my-app class="active">
If you defined styles in app-ngrx component, the styles will be applied to <app-ngrx> DOM element, NOT <my-app>. This particular configuration:
:host(.mine){
color:red;
}
Will be applied to the host element that has .mine class:
<app-ngrx class="active">
:host-context
Now, :host-context is also applied to the host element, but the function (parenthesis) takes a selector that is checked not against the host element itself, but against all ancestors up to document root. If such element is found, the styles are applied.
For example, this selector
:host(.mine){
color:red;
}
matches such structure:
<my-app class="mine">
whereas, this selector:
:host-context(.mine){
color:red;
}
matches this structure:
<div class="mine">
...
<my-app>
This is useful, if you want to apply styles to components view (shadow root) conditionally. This makes h2 always bold:
h2 {
font-weight: bold;
}
whereas this
:host-context(.make-inner-components-bold) h2 {
font-weight: bold;
}
makes them bold only if your component is inside an element with class .make-inner-components-bold.

:host { ... } selects the component itself
:host(.mine) { ... } selects the component itself when it has class="mine" set
:host-context(.mine) { ... } selects the component itself when one of its ancestors has class="mine" set
See also https://angular.io/docs/ts/latest/guide/component-styles.html
#Component({
selector: 'h3',
styles: [':host(.mine) { color: red; }],
template: '<ng-content></ng-content>'})
class MyH3Component{}
<h3 class="mine">this is red</h3>
<h3>this is black</h3>
or with :host-context
#Component({
selector: 'h3',
styles: [':host-context(.mine) { color: red; }],
template: '<ng-content></ng-content>'})
class MyH3Component{}
<body class="mine">
<my-app><my-app>
<body>
AppComponent
template: '<h3>this is red</h3>'
or with class="mine" set
<body>
<my-app><my-app>
<body>
AppComponent
template: '<h3>this is black</h3>'
update
If you want to style the content of a child component (instead of the child component itself) you can use /deep/
:host child /deep/ h3 {
color: red;
}
update 2
::slotted is now supported by all new browsers and can be used with `ViewEncapsulation.ShadowDom
https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted

Related

How to stop css bleeding in angular 11.2

I am working on a angular 11.2 project and I see that CSS of a component is getting applied to another component which has the same css selector name. How can I stop this? please help
If you apply the styles in Angular #component, it should be applied to that component scope only.
https://angular.io/guide/component-styles
#Component({
selector: 'app-root',
template: `
<h1>Tour of Heroes</h1>
<app-hero-main [hero]="hero"></app-hero-main>
`,
styles: ['h1 { font-weight: normal; }']
})
export class HeroAppComponent {
/* . . . */
}
Use encapsulation in your component declaretion
#Component({
selector:'app-component',
templateUrl:'app.compionent.html',
encapsulation:ViewEncapsulation.None,
})
My solution was to use ViewEncapsulation.None but to use targeted css. ie using specificity. for example if i had a component structure as below:
<div class="parent">
<div class="child">
<span></span>
</div>
</div>
My css would be :
.parent .child > span { ...some css here}
The reason not to use Emulated is that, some lib components can't be targeted with "None" as ViewEncapsulation. hence we need to set it to None and then follow this approach.

Angular 7: Why class style is not applied to DOM component?

I have a component billing that includes ngb-tabset component of Ng Bootstrap.
ngb-tabset has the following DOM element:
<ngb-tabset _ngcontent-c3="" class="content">
<!-- Tabset links-->
<div class="tab-content">
<div _ngcontent-c3="">
<!-- Content -->
</div>
</div>
</ngb-tabset>
<div class="tab-content"> is dynamically displayed according the selected tab.
In my billing.component.scss, the following snippet doesn't work:
.tab-content {
padding-right: 120px;
}
The CSS code is correctly compiled and, normally, seen by navigator but not effect on the item.
But if I apply it on the item outside the component, the code works correctly.
Any idea about this strange behavior?
Method 1 - Set style classes in the tab content template
View encapsulation isolates the CSS styling of each component, preventing the parent component CSS from affecting the child components. The preferred solution in the present case would be to set the style classes in the tab content template definition. Here is an example:
<ngb-tabset>
<ngb-tab title="Simple">
<ng-template ngbTabContent>
<div class="my-style-1">
<p>Some content</p>
</div>
</ng-template>
</ngb-tab>
...
</ngb-tabset>
.my-style-1 {
padding-right: 120px;
color: magenta;
font-style: italic;
}
See this stackblitz for a demo.
Method 2 - Use the ::ng-deep pseudo-class selector
Another method is to use the ::ng-deep shadow-piercing descendant combinator to style the content of the NgbTabset child component:
::ng-deep .tab-content {
padding-right: 120px;
}
See this stackblitz for a demo.
Method 3 - Turn off view encapsulation
Alternatively, you could turn off the view encapsulation of the parent component:
import { Component, ViewEncapsulation } from '#angular/core';
#Component({
...
encapsulation: ViewEncapsulation.None
})
See this stackblitz for a demo.
Angular uses shadow DOM. That means it keeps DOM logics separate from other elements. Components have scoped styles of their own. They are encapsulated in a way that the styles should not effect globally. So, if you want to change ngb-tabset's style you have to come out of its scope. That's why you need to use ViewEncapsulation.None.
In #Component decorator use ViewEncapsulation.None
#Component({
selector: ....,
encapsulation: ViewEncapsulation.None,
styles: [...])}
And, you can use ng-deep also. But you should not use ng-deep because it will be deprecated. See here- https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep

I have a plunker. When I define my css globally, it works. When I define my css in my component, it fails. What's going on?

With reference to this plunker:
https://plnkr.co/edit/GWsbdDWVvBYNMqyxzlLY?p=preview
I have the same css specified in the styles.css file, and in the src/app.ts file.
If I comment in the css in styles.css and comment out the css in src/app.ts, it works.
styles.css:
/* If these are commented in, and the ones in src/app.ts are
* commented out, the three items are spaced appropriately. */
/***
md-toolbar-row {
justify-content: space-between;
}
md-toolbar {
justify-content: space-between;
}
***/
If I comment out the css in styles.css and comment in the css in src/app.ts, it fails.
src/app.ts:
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<md-toolbar color="primary">
<span><md-icon>mood</md-icon></span>
<span>Yay, Material in Angular 2!</span>
<button md-icon-button>
<md-icon>more_vert</md-icon>
</button>
</md-toolbar>
</div>
`,
// If these are commented in, and the ones in style.css are
// commented out, the three items are scrunched together.
/***/
styles: [
`md-toolbar-row {
justify-content: space-between;
}`,
`md-toolbar {
justify-content: space-between;
}`
]
/***/
})
export class App {
name:string;
constructor() {
this.name = `Angular! v${VERSION.full}`
}
}
I'm having trouble visualizing the difference between defining the css for the whole application, and for the specific component. Can someone tell me what's going on?
=================================
#bryan60 and #Steveland83 seem to indicate that the problem lies somewhere in the view encapsulation. And upon further investigation, it does in a sense.
If you look at the code below, you will see that the styles for md-toolbar and md-toolbar-row have an attribute attached. But the html for md-toolbar and md-toolbar-row does not match. Only md-toolbar has the attribute attached. md-toolbar-row doesn't. I have marked the relevant four lines with >>>>>.
So that's the problem but:
1. Do I report it to the material design people as an error?
2. Is there some workaround I can use today?
<html>
<head>
:
<script src="config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
<link href="https://rawgit.com/angular/material2-builds/master/prebuilt-themes/indigo-pink.css" rel="stylesheet">
<style>
>>>>> md-toolbar-row[_ngcontent-c0] {
justify-content: space-between;
}
</style>
<style>
>>>>> md-toolbar[_ngcontent-c0] {
justify-content: space-between;
}
</style>
<style>
.mat-toolbar {
display: flex;
: :
.mat-mini-fab,
.mat-raised-button {
outline: solid 1px
}
}
</style>
</head>
<body class="mat-app-background">
<my-app _nghost-c0="" ng-version="4.4.0-RC.0">
<div _ngcontent-c0="">
<h2 _ngcontent-c0="">Hello Angular! v4.4.0-RC.0</h2>
>>>>> <md-toolbar _ngcontent-c0="" class="mat-toolbar mat-primary" color="primary" role="toolbar" ng-reflect-color="primary">
<div class="mat-toolbar-layout">
>>>>> <md-toolbar-row class="mat-toolbar-row">
<span _ngcontent-c0=""><md-icon _ngcontent-c0="" class="mat-icon material-icons" role="img" aria-hidden="true">mood</md-icon></span>
<span _ngcontent-c0="">Yay, Material in Angular 2!</span>
<button _ngcontent-c0="" class="mat-icon-button" md-icon-button=""><span class="mat-button-wrapper">
<md-icon _ngcontent-c0="" class="mat-icon material-icons" role="img" aria-hidden="true">more_vert</md-icon>
</span>
<div class="mat-button-ripple mat-ripple mat-button-ripple-round" md-ripple="" ng-reflect-trigger="[object HTMLButtonElement]" ng-reflect-centered="true" ng-reflect-disabled="false"></div>
<div class="mat-button-focus-overlay"></div>
</button>
</md-toolbar-row>
</div>
</md-toolbar>
</div>
</my-app>
</body>
</html>
One of the Angular features is View Encapsulation which basically means that you can define styles scoped only to a specific component without affecting any other components.
By default styles are scoped only for the component they are referenced in, but you can choose to override that to make them available globally by setting your components encapsulation to None.
E.g.
import { Component, ViewEncapsulation } from '#angular/core';
#Component({
selector: 'component-that-shares-styles',
templateUrl: './component-that-shares-styles.component.html',
styleUrls: ['./component-that-shares-styles.component.scss'],
encapsulation: ViewEncapsulation.None // <-- Set encapsulation here
})
*Note that you will need to import ViewEncapsulation from #angular/core
Okay, with help from #Steveland83 and #bryon60, I came to a definite answer. The Material Design people are aware of this problem. They have made a writeup.
https://github.com/angular/material2/blob/master/guides/customizing-component-styles.md
Here's their summary:
Styling other components
If your component has view encapsulation turned on (default), your component styles will only
affect the top level children in your template. HTML elements belonging to child components cannot
be targeted by your component styles unless you do one of the following:
Add the overriding style to you global stylesheet. Scope the selectors so that it only affects
the specific elements you need it to.
Turn view encapsulation off on your component. If you do this, be sure to scope your styles
appropriately, or else you may end up incidentally targeting other components elswhere in your
application.
Use a deprecated shadow-piercing descendant combinator to force styles to apply to all the child
elements. Read more about this deprecated solution in the
Angular documentation.
I don't want to use global css, or a deprecated solution. I guess I will style with classes, and not elements. If someone has a better idea, tell me!

Overriding the encapsulated :host-style of external component

Is it possible to override :host-styling of an external angular2-component?
We're making a library including a sidebar-component. This component has a default (fallback) background, but this should be overridable by css/theme used in the app.
#Component({
selector: 'sidebar',
styles: [`
:host { background-color: green; }
`],
template: `
<h1>sidebar</h1>
<ng-content></ng-content>
`
})
export class SideBarComponent { .... }
Main App css:
<style>
sidebar {background: red; color: yellow; }
</style>
This returns a sidebar with green background and yellow text, but I want a red background...
Edited:
As found on http://blog.angular-university.io/how-to-create-an-angular-2-library-and-how-to-consume-it-jspm-vs-webpack/: add an attribute to the body-tag:
<body override>
<app></app>
</body>
And in your css: use a selector for this attribute:
[override] hello-world h1 {
color:red;
}
This way, your css does not have to be parsed.
Previous solution:
I've found a solution myself: instead of linking my (theming) css-file in index.html, which isn't parsed, I imported this particular css-file in the app.component.ts annotation.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet/css" type="text/css" href="/assets/style/app.css" />
</head>
<body>
<app></app>
</body>
</html>
app.component.ts:
import { ... }
#Component({
selector: 'app',
styles: [`
#import "assets/style/theme.css";
`],
template: `
...`,
})
export class AppComponent {...}
theme.css:
sidebar {background: red; }
Its not possible to overwrite styles set in a component's styles this way. See point 3 on the "Using Component Styles" section in the Component Styles docs (https://angular.io/docs/ts/latest/guide/component-styles.html)
Our component's styles cannot be changed by changes to styles
elsewhere in the application.
Using :host-context is probably a nicer way to acheive this (although i have never tried myself).
You should try to check the same with host-context. As per documentation host-context works just like the function form of :host(). It looks for a CSS class in any ancestor of the component host element, all the way up to the document root. It's useful when combined with another selector.
In the following example, we apply a background-color style to all <h2> elements inside the component, only if some ancestor element has the CSS class theme-light.
:host-context(.theme-light) h2 {
background-color: #eef;
}

How to use component variable within css / style tag in Angular 2?

How can I use component variables within style TAG in Angular 2?
I do have an Angular 2 component for my header which I like to color depending on a users setting. Thus I'd like to assign an background and font color. While I know how to to this with an attribute binding to an element, I couldn't figure out how to use in a style tag.
Using attribute binding for style works well, however this gets pretty anoying for several subelements, especially if they are nested within other sub components. [ngStyle]= attribute is also only working on a single element.
<header id="header" [style.background-color]="changeBackground()">
<div>
Some Text
Some Link
<subcomponent></subcomponent>
</div>
<div> ... a lot mor stuff </div>
</header>
Thus I'd like to add something like
<style>
#header, #header a {
color: {{mycolor}};
}
</style>
to the html template. However this is not working
Similar Questions do not answer this question yet and only show attribute binding as a solution:
Angular2 dynamic change CSS property
Dynamically updating css in Angular 2
https://scotch.io/tutorials/all-the-ways-to-add-css-to-angular-2-components
https://coryrylan.com/blog/introduction-to-angular-2-ngclass-and-ngstyle
It looks to me like you are just creating a new component called 'subcomponent', why not do that?
subcomponent.ts:
import { Component } from '#angular/core';
#Component({
selector: 'subcomponent',
templateUrl: './subcomponent.html',
})
export class SubComponent {
mycolor = 'blue';
}
subcomponent.html:
<style>
#header, #header a {
color: {{mycolor}};
}
</style>
To your #Component object, add
styles:[ `#header, #header a {
color: {{mycolor}};
}`]
So for example:
#Component({
template: `<header id="header" [style.background-color]="changeBackground()">
<div>
Some Text
Some Link
<subcomponent></subcomponent>
</div>
<div> ... a lot mor stuff </div>
</header>`,
styles: [ `#header, #header a {
color: {{mycolor}};
}`
`]
})
Use NgStyle as explained in this answer
https://stackoverflow.com/a/41903349
in short
<header id="header" [ngStyle]="getStyle()">
<div>
Some Text
Some Link
<subcomponent></subcomponent>
</div>
<div> ... a lot mor stuff </div>
</header>
and
getStyle(): any {
return {"background-color" : this.changeBackgroundColor()};
}

Resources