Angular 2 - innerHTML 样式

Angular 2 - innerHTML styling

我从 HTTP 调用中获取了 HTML 代码块。我将 HTML 块放在一个变量中,并使用 [innerHTML] 将其插入我的页面,但我无法为插入的 HTML 块设置样式。有人对我如何实现这一目标有任何建议吗?

@Component({
  selector: 'calendar',
  template: '<div [innerHTML]="calendar"></div>',
  providers: [HomeService], 
  styles: [`h3 { color: red; }`]
})

我要设置样式的HTML是变量“calendar”中包含的块。

更新 2 ::slotted

::slotted 现在受到所有新浏览器的支持,并且可以与 ViewEncapsulation.ShadowDom

一起使用

https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted

更新 1::ng-deep

/deep/ 已弃用并替换为 ::ng-deep

::ng-deep 也已标记为已弃用,但尚无可用的替代品。

ViewEncapsulation.Native 得到所有浏览器的正确支持并支持跨阴影 DOM 边界的样式时,::ng-deep 可能会停止。

原创

Angular 将各种 CSS 类 添加到 HTML 它添加到 DOM 以模拟阴影 DOM CSS 封装以防止组件进出样式。 Angular 还会重写您添加的 CSS 以匹配这些添加的 类。对于使用 [innerHTML] 添加的 HTML,这些 类 未添加并且重写的 CSS 不匹配。

作为解决方法尝试

  • 为CSS 添加到组件
/* :host /deep/ mySelector { */
:host ::ng-deep mySelector { 
  background-color: blue;
}
  • 为 CSS 添加到 index.html
/* body /deep/ mySelector { */
body ::ng-deep mySelector {
  background-color: green;
}

>>>(和等价物/deep/,但 /deep/ 与 SASS 配合使用效果更好)和 ::shadow 已添加到 2.0.0-beta.10 .它们类似于影子 DOM CSS 组合器(已弃用)并且仅适用于 encapsulation: ViewEncapsulation.Emulated,这是 Angular2 中的默认值。它们也可能与 ViewEncapsulation.None 一起使用,但随后仅被忽略,因为它们不是必需的。 在支持跨组件样式的更高级功能之前,这些组合器只是一个中间解决方案。

另一种方法是使用

@Component({
  ...
  encapsulation: ViewEncapsulation.None,
})

对于阻止您的 CSS 的所有组件(取决于您添加 CSS 的位置以及您要设置样式的 HTML 的位置 - 可能是 您应用程序中的所有 个组件)

更新

Example Plunker

如果您尝试为 Angular 组件内动态添加的 HTML 元素设置样式,这可能会有所帮助:

// inside component class...
    
constructor(private hostRef: ElementRef) { }
    
getContentAttr(): string {
  const attrs = this.hostRef.nativeElement.attributes
  for (let i = 0, l = attrs.length; i < l; i++) {
    if (attrs[i].name.startsWith('_nghost-c')) {
      return `_ngcontent-c${attrs[i].name.substring(9)}`
    }
  }
}
    
ngAfterViewInit() {
  // dynamically add HTML element
  dynamicallyAddedHtmlElement.setAttribute(this.getContentAttr(), '')
}

我的猜测是不保证此属性的约定在 Angular 的版本之间稳定,因此在升级到新版本的 运行 时可能会遇到此解决方案的问题Angular(尽管在那种情况下更新此解决方案可能微不足道)。

您需要遵循的简单解决方案是

import { DomSanitizer } from '@angular/platform-browser';

constructor(private sanitizer: DomSanitizer){}

transformYourHtml(htmlTextWithStyle) {
    return this.sanitizer.bypassSecurityTrustHtml(htmlTextWithStyle);
}

我们经常从我们的 CMS 中提取内容 [innerHTML]="content.title"。我们将必要的 类 放在应用程序的根 styles.scss 文件中,而不是组件的 scss 文件中。我们的 CMS 特意去除了内联样式,因此我们必须准备好 类 供作者在其内容中使用。请记住,在模板中使用 {{content.title}} 不会从内容中呈现 html。

如果您使用 sass 作为样式预处理器,您可以通过以下方式切换回原生 Sass 开发依赖编译器:

npm install node-sass --save-dev

以便您可以继续使用 /deep/ 进行开发。

Günter Zöchbauer 推荐的版本很好用,但我还有一个补充。在我的例子中,我有一个无样式的 html-element 并且我不知道如何设置它的样式。因此,我设计了一个管道来为其添加样式。

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';


@Pipe({
    name: 'StyleClass'
})
export class StyleClassPipe implements PipeTransform {

    constructor(private sanitizer: DomSanitizer) { }
    transform(html: any, styleSelector: any, styleValue: any): SafeHtml {
        const style = ` style = "${styleSelector}: ${styleValue};"`;
        const indexPosition = html.indexOf('>');
        const newHtml = [html.slice(0, indexPosition), style, html.slice(indexPosition)].join('');
        return this.sanitizer.bypassSecurityTrustHtml(newHtml);
    }

}

然后您可以像这样向任何 html-element 添加样式:

<span [innerhtml]="Variable | StyleClass: 'margin': '0'"> </span>

与:

Variable = '<p> Test </p>'

任何想要的人只需将某种样式应用于内部HTML :

关注Create a safe HTML pipe

您可以像这样用 CSS 样式连接 HTML 字符串:

return this.sanitizer.bypassSecurityTrustHtml(value+='<style type="text/css">.image img { width: 100% }</style>');

这个 value 来自 transform(value, ...args)

我最初走 this.sanitizer.bypassSecurityTrustHtml() 路线,并将封装设置为 ViewEncapsulation.NONE,但有 2 个问题:

  1. ViewEncapsulation.NONE 导致我的组件出现其他样式问题
  2. 我的“安全”html 似乎无法使用 css 变量,即 var(--blue)

这对我有用(无需更改任何其他内容): InsertAdjacentHTML

模板:

<div id=template></div>

代码:

ngOnInit() {
  const el = document.getElementById('template');
  el.insertAdjacentHTML('afterbegin', `<span style="color: var(--blue)">hello</span>`);
}

免责声明:在我的例子中,我从配置文件中解析 html。您不会希望用户输入 html.

走这条路

最简单直接的方法是使用位于 angular 项目 src 文件夹中的全局样式文件。

假设组件选择器是:app-my-component

在 app-my-component 模板中向承载 innerHtml 内容的元素添加一个 class:

<div class="innerhtml-class" [innerHTML]="variable.innerHtml"></div>

添加到全局样式文件:

app-my-component { 
 .innerhtml-class { 
   declaration goes here 
 } 
}

使用以下方法允许 innerhtml 中的 CSS 样式。

import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
.
.
.
.
html: SafeHtml;

constructor(protected _sanitizer: DomSanitizer) {
   this.html = this._sanitizer.bypassSecurityTrustHtml(`
        <html>
        <head></head>
        <body>
           <div style="display:flex; color: blue;">
              <div>
                 <h1>Hello World..!!!!!</h1>
              </div>
           </div>
        </body>
        </html>`);
}

示例代码 stackblitz

用下面的方法直接写在HTML。 https://gist.github.com/klihelp/4dcac910124409fa7bd20f230818c8d1

如果您的动态样式有限,则使用 inline CSS variables 是替代解决方案。

// file.ts
someVarWithHtml = 'Hello <span class="dynamic">World</span>';

// file.ng.html
<div [style]="'--my-var: ' + value"
     [innerHTML]="someVarWithHtml"></div>

// style.css
.dynamic {
  background: var(--my-var);
}