Styling angular material components from child components - css

HomeComponent templates includes a router outlet
/* home.component.ts */
...
<mat-sidenav-container>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
that will render a ListComponent which style should set the overflow of mat-sidenav-content.
Why is the following not working?
/* list.component.css */
:host-context(mat-sidenav-content) { /* same for .mat-sidenav-content */
overflow: unset !important;
}
From my understanding the selector should pick any mat-sidenav-content traversing all the DOM to the root.

I'm afraid this isn't how :host-context works. Host context is for applying styles to the component host conditionally based on it's ancestors. In your example the list component should have overflow: unset but if there was another instance of it without mat-sidenav-content as an ancestor it would not have this. This can be useful for applying styles based on a theme as shown in the documentation here:
https://angular.io/guide/component-styles#host-context
I have included an example on stackblitz with your overflow css and I also set the text to be red in that case so it's more obvious and you can see how it applies to one and not the other. Unfortunately there is not a way to do what I believe you are trying to do which is apply a style to a parent when the component is inside that parent.
The example I have also has ::ng-deep in the other component for completeness, as I have seen used to apply styles to other components by some. I would recommend you stay away from this as well as this style will remain after being first applied(try going to that link and then back to list) and ::ng-deep is deprecated anyway.
https://stackblitz.com/edit/angular-material-sidenav-generate-nav-5fvfjq?file=src/app/list/list.component.css
Edit: The stackblitz has been updated to include an example of :host
It's better to think of the host element in HTML terms rather than as a component, so in the dom <app-list> is the host element rather than what's in the template. If you wanted to style stuff in there you wouldn't need to use :host. I've updated my stackblitz example to include host and if you inspect the css you might get a better idea of how it works.
The HTML looks like this
<app-list _nghost-qnl-c20="">
<p _ngcontent-qnl-c20=""> This text should be red only when inside mat-sidenav-content. The font family is set by host.
</p>
</app-list>
And the css:
mat-sidenav-content[_nghost-qnl-c20], mat-sidenav-content [_nghost-qnl-c20] {
color: red;
}
[_nghost-qnl-c20] {
font-family: Arial, Helvetica, sans-serif;
}
So you can see that host uses an attribute generated by angular and :host by itself is simply that attribute and :host-context(element) becomes element [attribute] in the css.

Related

Angular - How To Organize CSS Overrides Across Components For The App

I'm relatively new to angular. In the process of converting a React app to Angular just for learning purposes. I have a parent component that has a button (Custom Button Component). This button when loaded in the parent should be hidden and on hover should show the button. So you probably get an idea that I have some CSS selectors related to the parent component that override the child CSS. First should be display: none and then on hover I change it to display: flex
So the first problem I encountered was that I could not override the child CSS from the parent CSS. After reading all kinds of posts I moved the CSS overrides from the parent CSS to the global stylesheet and also added encapsulation: ViewEncapsulation.None to the child component.
Next thing I noticed is that the align-items: center was not working on the child. First thought I had that guess I have to add that part to the global styles also? but what I really need to know is that is this the norm in Angular? If yes, then some things don't make sense to me. These styles are really not global. They are only related to the parent component then it seems kind of weird to add those to the global stylesheet.
In regards to the align-items not aligning the child (custom-button), I believe that happens because of the extra div being added around the button. So how do you handle such situations?
Appreciate any advice/tips.
Thank you!
You can overwrite children CSS classes from the parent componet. this is the way:
Assuming your child component have this CSS
.child-class {
background-color: blue;
}
When you use this component the background color will be blue. But if you want to change that color to RED. In the parent component where you want the change you need to do this:
In your parent component
:host {
::ng-deep{
.child-class {
background-color: red;
}
}
}
:host this refers to the component's HTML tag (that is created by Angular, in your case the tag of the component that contains the app-custom-button). Also you can apply css to the component tag.
for example:
:host{
width: 100vw;
height: 100vh
}
And with ::ng-deep you can overwrite ALL styles inside your compoent. Does not matter if is a style from your child compoenent, grandchild, great-great-grandson, etc... even if its a component from an external library.
So... For example you can have the "custom background color as blue" then in one component you can keep that color but in other component you can change the color to red and in other component you can change the color to green....
Angular have the concept of ViewEncapsulation. By default, the value is set to ViewEncapsulation.Emulated and the css you put in the component is specific to the component and only to this component. The CSS will not be applied to the child components.
You can switch to ViewEncapsulation.None and you will disable this behavior and all the css rules in your css file will be applied to all your components in the application, and maybe you don't want this behavior. That's why I advice you to leave this option.
The other option you got is to put your specific css rule in src/style.css (if you didn't modify the default path). All css rules put in this file will be applied for all the application and you can keep the ViewEncapsulation of your component.
For align-items, i think you are right : the app-custom-button is wrapping your button, so you need to set a width: 100% to your button, then eventualy resize your app-custom-button

How to refer to an Angular component's full template setting CSS without referring to any tags?

My usual setup for each view is an outer DIV that I style as the base background etc.
<div class="outer">
<!-- Actual stuff in here -->
</div>
Then, in the SASS, I refer to it like so.
div.outer { ... }
That adds one lever of indent and seems like an unnecessary (though minor) increment in complexity. So I wonder if it's possible to add a style to the template itself. Partly, to lower the complexity. Partly, because I'm going to have text-only elements with no tags at all.
Is it possible to set the style of template from SASS files if there are no tags, only text in it?
You can apply styling to the component host element with the :host selector:
:host {
color: red;
}
See this stackblitz for a demo.

What scoped CSS selector allows me to override the mdDialog container width?

Given a typical Angular Material dialog, which has a max-width of 80vw set on .mat-dialog-container, how can I formulate a selector to override it? I'd like a true full-width dialog.
The problem is scoping--Angular-CLI compiles component CSS with scope attribute selectors.
Therefore, this:
.parent .child
becomes:
.parent[some_attr] .child[some_other_attr]
However, this particular element doesn't seem attached to any component--it doesn't have a dynamically-generated attribute on it.
I've attempted overrides in both the dialog stylesheet and the host component's stylesheet with no success.
Angular special selectors
Dialog Plunkr
Let me try again. I'm not doing a good job of explaining the issue.
Let's say I add this to my host component stylesheet:
.mat-dialog-container {
max-width: 100%;
}
I have a build watch running, so the app is recompiled. The CSS output is now this:
.mat-dialog-container[_ngcontent-c6] {
max-width: 100%;
}
However, that element doesn't actually have the attribute _ngcontent-c6 on it. That attribute is applied to other elements which are inside siblings of ancestors of .mat-dialog-container. The selector is just wrong.
If I add that CSS to the dialog component's stylesheet, something similar happens, but with a different attribute ID.
If you can add an id to your main body tag and you want it to be on all of these dialogs you can use this
<style>
#bodyID .mat-dialog-container {
max-width:100vw;
background-color: blue;
}
</style>
It will override the current style, at least in the plunker you supplied.
If you need specific style for each dialog, did you look into this?
how-to-style-child-components-from-parent-components-css-file
You don't need a body id, because as you've mentioned the selector is rewritten by Angular such that it stops matching the element altogether.
But yeah it seems the only way around this is to just throw your hands up and forget about component stylesheet scoping and add your CSS rule to the page stylesheet. The caveat, of course, is that this rule needs to be added to every page that uses the component, which can be seen as absurd depending on what your component is intended to be used for and by whom.

Overriding the encapsulated CSS of external component

I was wondering how to override the encapsulated CSS of an external component.
So I am using material2 in my project and the tabs component has a the attribute overflow set on tab-body. Is it possible to override the overflow value?
You can use the special css /deep/ instruction. See the documentation
So, if you have
app
sub-component
target-component
<div class="target-class">...</div>
You can put in your apps css (or less):
/deep/ .target-class {
width: 20px;
background: #ff0000;
}
Obviously, you can put this css fragment in sub-component as well.
From this article
Although the style of a component is well isolated, it can still be easily overridden if necessary. For that, we just need to add an attribute to the body of the page:
<body override>
<app></app>
</body>
The name of the attribute can be anything. No value is needed and the name override makes it apparent what its being used for. To override component styles, we can then do the following:
[override] hello-world h1 {
color:red;
}
Where override is the attribute, hello-world is the target component, and h1 is whatever you are trying to restyle. (get this right or it wont work).
Your component hello-world would be
selector: 'hello-world',
styles: [`
h1 {
color: blue;
}
`],
template: ` <h1>Hello world</h1> `
I think this is the most elegant way.
Alternatively if you are building a library of some sort, you can reset the styling altogether by doing something fancy in your css like:
:host-context(.custom-styles) {
//.. css here will only apply when there is a css class custom-styles in any parent elem
}
So then to use your component you'd use
<hello-world class="custom-styles">
But this is way less convenient than the first option.
::ng-deep .tag-or-css-class-you-want-to-override {
/* Add your custom css property value. */
}
The syntax ::ng-deep is used to override outside css class or tags without using ViewEncapsulation.None.
I see variations of this question a lot and since this is the top question on the subject I want to give the simplest answer. ng-deep and similar functionality is deprecated, so it's best to just rely on vanilla CSS.
Simply create a CSS selector with a higher specificity.
Most people (including myself) get hung up trying to do that because they don't understand two things:
Angular View Encapsulation
CSS Specificity
Angular View Encapsulation
View Encapsulation ensures CSS within a component only affects that component. To affect other components, you need some global CSS. You can do this by using a global style file like styles.css or by disabling View Encapsulation on a component.
#Component({
...
encapsulation: ViewEncapsulation.None
})
CSS Specificity
When two selectors select the same element, the CSS that actually gets applied is based on specificity: https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
You can increase specificity by simply adding more elements to your CSS selector. For example p.className is more specific than just .className. If you're lazy, you can just repeat a class name to increase specificity. .className.className is more specific than .className.
So to override any CSS in an Angular project, go into styles.css and repeat the class selector until your CSS has a higher specificity than the original.
.className.className.className {
color: red;
}
Didn't work? Add another .className.
Just check the class that is being applied to the tabs by the external component (use Inspector or any other tool). In your style css file, add the same name of the class for the tabs and set the overflow property along with adding !important to it to make sure it overwrites the previous one. Also make sure your css link to the page is added after the external component css link if any.
Hope this helps.
::ng-deep .css-class-you-want-to-override{
/*your custom css property value. like below */
background: white !important;
}

Dojo FilteringSelect CSS Styles

What are all the css style classes that has to be changed to restyle dojo filtering select ?
Note: I am using claro theme.
I want to
1.Set the style for one particular filteringselect with id QuickSearchPane_SelectBox
2.Set the style for all other filteringselect
I found a few like:
.claro .dijitTextBox .dijitInputInner
.claro .dijitInputField .dijitPlaceHolder
.claro .dijitSelect
But these are not giving the desired effect. I am not even able to change the background colors.
For Menu
[dijitpopupparent="QuickSearchPane_SelectBox"] > .dijitComboBoxMenu .dijitMenuItem
This seems to work.
You can use the following CSS class to start styling your dijit/form/FilteringSelect;
This example will style all instance of dijit/form/FilteringSelect:
https://jsfiddle.net/ofgcd24n/
.dijitInputInner {
background-color: green !important;
}
.dijitMenuItem {
background-color: orange;
}
This other example below will style only ONE instance of dijit/form/FilteringSelect, please note the use of Descendant combinator as selector (where you use the ID for your widget DOM):
#widget_stateSelect .dijitInputInner {
/* your style*/
}
Generally you can use (in Chrome Dev Tool) Event Listen Breakpoints for click/mouse down, so when you open you FilteringSelect, you can block execution, and check with the inspector its HTML structure and see additional CSS classes you want to override with your styles.
More about CSS selector:
https://www.w3.org/TR/css3-selectors/
If you need more details, please post your HTML and CSS and desired layout so we can work out a specific solution.

Resources