I'm having troubles using Angular router-oulet because it adds new elements to the DOM and it affects the CSS rules that are applied because it breaks the parent-child relationship of some elements. Here's an example of what I mean:
As you can see, after each <router-outlet></router-outlet> is the inserted component selector (app-fwc-dashboard-main and app-fwc-policy-main in the code) and all the component template markup comes underneath it. This makes that there are some CSS rules that are not applied and the layout is broken.
I've been comparing the code from one version to the other and I've realized that the component selector has the class ng-star-inserted so I've tried to add the missing CSS rules, like this:
.grid-y > .ng-star-inserted > .auto, .grid-y > .ng-star-inserted > .shrink {
height: auto;
}
.grid-y > .ng-star-inserted > .cell {
width: auto;
}
(etc)
But, for some reason I can't find, it didn't work. So my question is: Is there any way to tell Angular router outlet to inject ONLY the component template markup and avoid adding the selector?
Or is there any other suggestion for this problem? Thanks in advance!
I don't think this is possible.
As I see it you have two possible solutions:
Remove the > selectors in your css.
Include the component selector in your css.
If these doesn't work for you I suggest rewriting the code in a manner which makes it easier to work with.
You can use attribute selector for Component.
Add classes using #HostBinding and css selector :host inside styles.
#Component({
selector: '[attributeSelector]',
template: 'red text',
styles: [':host {color: red}'],
})
class A {
#HostBinding('class') class = 'boundClass';
}
In this case router-outlet will create something like
<router-outlet></router-outlet>
<div attributeSelector class="boundClass" nghost_1>red text</div>
Ilia Volk's answer showed me the right track to find the solution. I changed the component selector to this:
selector: '[app-vpn-component].grid-y.medium-grid-frame',
This way, the rendered markup is:
<div app-fwc-vpn-main="" class="grid-y medium-grid-frame ng-star-inserted">
...
</div>
And now the layout looks as before.
Related
I'm relatively new to Angular, and I have a doubt about component stylesheets.
I have an Angular 12 app and created a component named my-component. The template of the component in question is something like this:
my-component.html
<div>
...some html...
<some-other-angular-component></some-other-angular-component>
...some other html...
</div>
some-other-angular-component is another component, either from the app itself or a third party library.
Now, what I want to do in my-component is apply some CSS rules to the contents of some-other-angular-component. I know that the HTML it generates contains classes that I can target, so I tried to add this to my component CSS:
my-component.scss
.some-other-angular-component-inner-class {
background-color: red;
}
However, this doesn't work, it appears that the component's CSS file only applies rules to the HTML defined directly in the component's template, not the HTML generated by sub-components.
Is there a way to make this work? I find myself having to add my CSS to the webapp's main style.scss file, even when I want to apply the rule only to the particular some-other-angular-component instance inside of my-component. It makes styling confusing and needlessly fragmented. Is this intended, or what am I missing?
I think you may want to look into View Encapsulation.
#Component({
selector: 'app-no-encapsulation',
template: `
<h2>None</h2>
<div class="none-message">No encapsulation</div>
`,
styles: ['h2, .none-message { color: red; }'],
encapsulation: ViewEncapsulation.None,
})
export class NoEncapsulationComponent { }
These styles will be added to head and will be applicable to other components as well if style rule matches.
Please note, with this you are only enabling this behaviour for just this component. Chances of overlapping CSS rules is still there but is lot less in comparison to directly putting styles in style.css.
I will also suggest that you add .class or #id attribute in mark up to ensure that your rules don't overlap by default.
For example:
.my-component .rule-one {
}
It will ensure that my rules are only applied are on component that has this class applied on it.
I'm using react-select and I'm trying to style the Select component like written in the DOCS but adding a class with className does not work. The class tag is added to the DOM element's classes but doesn't affect the element itself. The theme stay the same.
If I add a class like this:
<Select
className='my-class'
{...selectProps}
/>
The DOM element looks like this:
<div class="my-class css-2b097c-container">
...
</div>
Which means that the default class css-2b097c-container of react-select will always "override" my custom class.
I've tried to use the classNamePrefix option but did not work either :(
In order to keep my UI design complete I need to design it with classes and not with inline styling!
classNamePrefix works for most but not all components which is kinda annoying to me. Another solution is to add your own className as stated in your question but refer to both of them in your css using [attribute*=value] selector to increase specificity.
<Select
className="mySelect"
options={options}
/>
.mySelect[class*="-container"] {
background-color: lemonchiffon;
padding: 10px;
}
Live Example
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.
I have been messing with ViewEncapsulation and ::ng-deep recently and ran into two separate issues which piqued my curiosity.
Suppose I have a component:
<div>
<tooltip></tooltip>
</div>
From the outside, I can style elements inside of tooltip by doing something like:
::ng-deep .tooltip-content {}
Because ::ng-deep is deprecated however, I began exploring alternatives. My first idea was to set ViewEncapsulation.None on the tooltip but then that would affect the rest of the front-end that used the tooltip. It wasn't a good option.
My second idea was to set ViewEncapsulation.None on the host element and then style the tooltip normally, without using ::ng-deep but instead, appending !important to the required styles.
How does this work though? If the tooltip retains its ViewEncapsulation, how am I still able to pierce the shadow boundary?
I use always ViewEncapsulation.None for reusable components. To avoid side-effect to rest of project, i use deep-nesting selector with name of component tag.
HTML:
<test-component>
<div class="test">
<tooltip></tooltip>
</div>
</test-component>
scss:
test-component{
.test { ... }
tooltip { ... }
}
with that all tooltip tag in test-component ha unique style with out any side effect
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;
}