I am still just learning in Angular, CSS, and HTML (all three are new), so have some patience with me please.
I received some code, and was given the task to fix some formatting.
Here is the problem:
When the page first loads, the page header has some padding. See picture below on the left.
However, when I navigate to another page, which has this code:
/* Removing padding and scroll bar from main page */
::ng-deep html > body > main#app-content {
overflow-y: hidden;
padding: 0;
}
and then navigate to any other component/page, the padding is gone and everything is moved all the way to left of the screen, which is very annoying. See picture below on the right. Note: someone told me that this is the code that is causing this situation, and I actually have no idea what it is actually doing (besides setting the padding and y-scroll).
This picture shows two components/pages before I access the page with the code above (shown on the left side in the picture), and then after I navigate to that page with the code above (shown on t eh right side in the picture). Note the green line is for reference to show how the padding is gone.
So I would like to have the original padding/formatting back when I navigate back to it after I access the page with the code above.
Also, can someone explain to me why it's doing this? And if possible, what does the code actually mean? Here are some specific questions:
- How can I stop this from happening on another page?
- What does "::ng-deep html > body > main#app-content" mean?
- What does the greater sign do?
CodePudding user response:
TLDR: Here is a stackblitz with an example of how to edit a global style using a service rather than ::ng-deep
. https://stackblitz.com/edit/angular-ivy-p4pkdu?file=src/app/app.component.html
::ng-deep
is an angular feature that promotes the following css to apply globally (everywhere in your application). It should really be avoided, as there is usually a better way to apply global styles. This feature is actually being deprecated, I'll put an alternative at the end of this answer.
html > body > main#app-content
is just a CSS selector. In this case we are selecting the main
element with id
app-content
, which has body
as a parent, which has html
as a parent. Here is a good reference for CSS syntax: https://www.w3schools.com/cssref/css_selectors.asp.
So we are applying these css styles to an html element of type main
and with id app-content
, the style is applied globally, so it will still persist after the encapsulating component is destroyed.
A better alternative to ::ng-deep
is to use a service to edit global styles. First off, any global styles should be stored or imported into the global styles file, usually called styles.css
in an angular project. If you only need the style in one component, you can put this css in the respective component css file instead. We declare it as a class so we can add it to an element dynamically.
In styles.css
.noPaddingOrScrollbar {
overflow-y: hidden;
padding: 0;
}
Then generate a service with the cli using ng g service <serviceName>
. For example, to generate a service named globalStyleService
in a folder called services
we do ng g service services/global-style
. We'll add a boolean to our service to indicate whether we want the style applied or not.
One caveat is that we need to use setTimeout
to set the boolean, to avoid the dreaded NG0100: Expression has changed after it was checked
error. setTimeout
will default to a timeout of zero, but will still delay the code execution until after Angular finishes a round of change detection.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class GlobalStyleService {
private _noPaddingOrScrollbar = false;
set noPaddingOrScrollbar(value: boolean) {
//Delay setting until after change detection finishes
setTimeout(() => (this._noPaddingOrScrollbar = value));
}
get noPaddingOrScrollbar() {
return this._noPaddingOrScrollbar;
}
constructor() {}
}
Now you need to find in what component this main#app-content
element is actually located. It'll be in the html file of one of the parent components. You can then inject the service into this parent component ts file, and set the class dynamically in the component's html file.
Parent component ts file
export class ParentComponent {
constructor(public globalStyle: GlobalStyleService) {}
...
}
We use the angular directive [class.className]="boolean"
to set the class dynamically.
Parent component html file
...
<main
id="app-content"
[class.noPaddingOrScrollbar]="globalStyle.noPaddingOrScrollbar"
></main>
...
Now you can add or remove this class from anywhere in your application. So in the component containing the hacky css, we inject the service, add the style during ngOnInit
and remove it during ngOnDestroy
. Of course remove the ::ng-deep
statement from the css file as well.
Child component ts file
export class MyComponent implements OnInit, OnDestroy {
constructor(private globalStyle: GlobalStyleService) {}
ngOnInit(): void {
this.globalStyle.noPaddingOrScrollbar = true;
}
ngOnDestroy(): void {
this.globalStyle.noPaddingOrScrollbar = false;
}
...
}