角度-如何删除< style> < head>中的元素组件销毁后 [英] Angular - How to remove <style> element from <head> after the component is destroyed

查看:95
本文介绍了角度-如何删除< style> < head>中的元素组件销毁后的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:我在Angular 5和Angular 6中都尝试了该示例.

Note: I tried the example in both Angular 5 and Angular 6.

问题

如果在Angular组件上使用'encapsulation: ViewEncapsulation.None',则在显示组件时,<style>元素将附加到<head>. <style>元素将永远不会被删除,即使在组件被销毁之后也是如此.这就是问题. 随着显示更多组件,<head>中有越来越多的<style>元素.最终,当对同一个html元素有全局CSS规则时,例如,这会导致冲突. body,根据我的示例.即使最后一个<style>块属于不再存在的组件,也将仅使用最后一个<style>块中的CSS.

The issue

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.

我想看到一个适当的解决方案,用于从DOM中删除某些组件附带的<style>元素.例如.触发组件的onDestoy功能时进行清理.

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.

我是Angular的新手,我偶然发现了这种有趣的行为.很高兴知道是否有一个简单的解决方法.

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.

示例: https://stackblitz.com/edit/angular-ukkecu

在我的应用程序中,我有3个包装器组件,它们将成为我应用程序的根元素.当时只会显示一个.显示的组件将确定整个网站的主题.它应包括全局样式,更具体而言,应包括全局样式(主题)的专用变体.因此,它们都具有'encapsulation: ViewEncapsulation.None'.每种全局样式都具有自己的引导程序和其他基于SASS变量的外部插件的已编译变体.因此,在这里封装是没有选择的,它们是全局样式和插件.

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.

该解决方案仅在显示其他组件并将<style>元素附加到<head>之前才可以正常工作.之后,将仅使用最后使用的组件中的样式,因为它的<style>位于最后,并且会覆盖以前的所有样式.

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.

推荐答案

在您的情况下,请忘记encapsulation,它对您的要求无济于事.而是使用共享服务,将其称为 style-service ,它将在文档头中添加/删除样式节点.

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.

您可以使用ngOnInit函数上的 style-service 来添加它们,而不是在@Component装饰器的stylesUrls中添加css样式,这将添加样式节点到文档头.一旦使用ngOnDestroy函数销毁了组件,您将使用 style-service 删除样式,这将从文档头部删除样式节点.

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 (来自您的演示)

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');
  }

}

从StackBlitz示例中进行工作(分叉)演示.

Working (forked) DEMO from your StackBlitz sample.

这篇关于角度-如何删除&lt; style&gt; &lt; head&gt;中的元素组件销毁后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆