Note: I tried the example in both Angular 5 and Angular 6.
If using 'encapsulation: ViewEncapsulation.None'
on an Angular component a <style>
element will be appended to the <head>
when component is being shown. The <style>
element will never get removed, even after the component is destroyed. This is the issue.
As more components are shown there are more and more <style>
elements in the <head>
. Ultimately this causes conflicts when there are global css rules for the same html element e.g. body
, as per my example. Only the CSS from the lastly appended <style>
block will be used, even if this last <style>
block belongs to the component that does not exist anymore.
I would like to see a proper solution for removing from the DOM the <style>
element that came along with some component. E.g. cleanup when onDestoy
function from the component is triggered.
I am new to Angular and I have just stumbled upon this interesting behavior. Would be good to know if there is a simple workaround.
EXAMPLE: https://stackblitz.com/edit/angular-ukkecu
In my app I have 3 wrapper components that will be the root element of my app. Only one would be shown at the time. Shown component will determine the theme for the entire website. It should include global styles, more specifically a dedicated variant of a global style (a theme). For that reason they all have 'encapsulation: ViewEncapsulation.None'
. Each global style have it's own compiled variant of bootstrap and other external plugins based on SASS variables. So having encapsulation here is no option, these are global styles and plugins.
The solution works fine the first time only until the other components are shown and <style>
elements are appended to the <head>
. After that only the styles from the lastly used component will be used because its <style>
came last and overrides any previous styles.
It seems like the only solution would be to reload the page, or not use components for switching the global theme.
Forget about encapsulation
in your case, it can't help you with your requirement. Instead use a shared service, lets call it style-service, that will add/remove the style nodes in the document head.
Instead of adding your css styles in the stylesUrls
of the @Component
decorator, you will add them by using the style-service on ngOnInit
function, which will add the style node to the document head. Once the component gets destroyed on ngOnDestroy
function, you will remove the style by using the style-service, which will remove the style node from the document head.
Enough said, lets see some code:
style.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class StyleService {
private stylesMap: Map<any, Node> = new Map();
private host: Node;
constructor() {
this.host = document.head;
}
private createStyleNode(content: string): Node {
const styleEl = document.createElement('style');
styleEl.textContent = content;
return styleEl;
}
addStyle(key: any, style: string): void {
const styleEl = this.createStyleNode(style);
this.stylesMap.set(key, styleEl);
this.host.appendChild(styleEl);
}
removeStyle(key: any): void {
const styleEl = this.stylesMap.get(key);
if (styleEl) {
this.stylesMap.delete(key);
this.host.removeChild(styleEl);
}
}
}
WrapperVariantRedComponent (from your demo)
import { Component, OnInit, OnDestroy, Renderer2 } from '@angular/core';
import { StyleService } from '../style.service';
declare const require: any;
@Component({
selector: 'app-wrapper-variant-red',
templateUrl: './wrapper-variant-red.component.html',
styleUrls: [ './wrapper-variant-red.component.css']
})
export class WrapperVariantRedComponent implements OnInit, OnDestroy {
constructor(private styleService: StyleService) { }
ngOnInit() {
this.styleService.addStyle('red-theme', require('../../theme/global-theme-variant-red.css'));
}
ngOnDestroy() {
this.styleService.removeStyle('red-theme');
}
}
Working (forked) DEMO from your StackBlitz sample.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With