【问题标题】:Angular - How to remove <style> element from <head> after the component is destroyedAngular - 如何在组件被销毁后从 <head> 中删除 <style> 元素
【发布时间】:2018-10-22 23:33:47
【问题描述】:

注意:我在 Angular 5 和 Angular 6 中都试过这个例子。

问题

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

我希望看到一个适当的解决方案,用于从 DOM 中删除与某些组件一起出现的 &lt;style&gt; 元素。例如。触发组件中的onDestoy 函数时进行清理。

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


示例

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

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

该解决方案仅在显示其他组件并将&lt;style&gt; 元素附加到&lt;head&gt; 之前第一次运行良好。之后,将只使用最后使用的组件中的样式,因为它的 &lt;style&gt; 排在最后并覆盖了之前的所有样式。


可能的解决方案

似乎唯一的解决方案是重新加载页面,或者不使用组件来切换全局主题。

【问题讨论】:

  • 如果您投反对票,我也很乐意听到原因。我清楚地描述了问题并设置了实例
  • 您不应该使用已加载的组件来编写全局主题。让服务(在应用程序级别)处理它。如果您愿意,这些组件可以告知服务该做什么。
  • 那么服务应该编译sass文件并注入全局样式?
  • 不,管理body 的任何组件也使用该服务,但动态设置样式(通过使用类或直接在模板中或其他方式)。就像任何其他组件样式本身一样。
  • 这是一个快速破解的 stackblitz 演示它:stackblitz.com/edit/angular-r7cjiy

标签: angular


【解决方案1】:

在您的情况下忘记encapsulation,它无法帮助您满足您的要求。而是使用共享服务,我们称之为 style-service,它将添加/删除文档头中的样式节点。

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

说了这么多,让我们看一些代码:

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 示例中工作(分叉)DEMO

【讨论】:

  • 您可以使用@bespunky/angular-zen 中的HeadService 来执行此操作:bs-angular-zen.web.app/docs/zen/additional-documentation/… 它非常灵活且易于使用。
  • 对于纯 CSS 可以正常工作,但是在使用 SASS 文件进行样式设置时有什么解决方案可以工作吗?
  • @Lars 这是个好问题!不幸的是,我不知道答案,我必须深入挖掘才能找到答案。如果你能找到答案,如果你能分享它,我会很高兴。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-28
  • 2014-03-23
  • 2019-07-28
  • 1970-01-01
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多