Angular HTML 绑定

Angular HTML binding

我正在编写一个 Angular 应用程序,我有一个 HTML 响应要显示。

我该怎么做?如果我简单地使用绑定语法 {{myVal}} 它编码所有 HTML 个字符(当然)。

我需要以某种方式将 divinnerHTML 绑定到变量值。

如果我在这里遗漏了重点,我深表歉意,但我想推荐一种不同的方法:

我认为最好 return 来自服务器端应用程序的原始数据并将其绑定到客户端的模板。这使得请求更加灵活,因为您只从您的服务器 returning json。

对我来说,如果您所做的只是从服务器获取 html 并将其 "as is" 注入 DOM.

我知道 Angular 1.x 有一个 html 绑定,但我还没有在 Angular 2.0 中看到对应的绑定。他们可能会稍后添加它。无论如何,我仍然会考虑为您的 Angular 2.0 应用程序提供数据 api。

如果您有兴趣,我这里有一些示例,其中包含一些简单的数据绑定:http://www.syntaxsuccess.com/viewarticle/angular-2.0-examples

Please refer to other answers that are more up-to-date.

这对我有用:<div innerHTML = "{{ myVal }}"></div>(Angular2,Alpha 33)

根据另一个 SO:Inserting HTML from server into DOM with angular2 (general DOM manipulation in Angular2),"inner-html" 相当于 Angular 1.X

中的 "ng-bind-html"

在 angular2@2.0.0-alpha.44:

Html-使用 {{interpolation}} 时绑定将不起作用,请改用 "Expression":

无效

<p [innerHTML]="{{item.anleser}}"></p>

-> 抛出错误(插值而不是预期的表达式)

正确

<p [innerHTML]="item.anleser"></p>

-> 这是正确的方法。

您可以向表达式中添加其他元素,例如:

<p [innerHTML]="'<b>'+item.anleser+'</b>'"></p>

提示

HTML 使用 [innerHTML] 添加(或通过 element.appenChild() 等其他方式动态添加)不会被 Angular 以任何方式处理,除了清理安全目的。
只有当 HTML 被静态添加到组件模板时,这些事情才有效。如果你需要这个,你可以在运行时创建一个组件,就像

中解释的那样

正确的语法如下:

<div [innerHTML]="theHtmlString"></div>

Documentation Reference

为了获得完整的答案,如果您的 HTML 内容在 component variable 中,您还可以使用:

<div [innerHTML]=componentVariableThatHasTheHtml></div>

[innerHtml] 在大多数情况下是一个很好的选择,但是对于非常大的字符串或者当您需要 html.

中的硬编码样式时它会失败

我想分享其他方法:

你需要做的就是在你的 html 文件中创建一个 div 并给它一些 id:

<div #dataContainer></div>

然后,在您的 Angular 2 组件中,创建对此对象的引用(此处为 TypeScript):

import { Component, ViewChild, ElementRef } from '@angular/core';

@Component({
    templateUrl: "some html file"
})
export class MainPageComponent {

    @ViewChild('dataContainer') dataContainer: ElementRef;

    loadData(data) {
        this.dataContainer.nativeElement.innerHTML = data;
    }
}

然后只需使用 loadData 函数将一些文本附加到 html 元素。

这只是您在 Angular 环境中使用本机 javascript 执行此操作的一种方式。我不推荐它,因为它使代码更混乱,但有时也没有其他选择。

另见

正在使用 Angular v2.1.1

<div [innerHTML]="variable or htmlString">
</div>

Angular 2.0.0 和 Angular 4.0.0 最终版

为了安全的内容只是

<div [innerHTML]="myVal"></div>

DOM消毒剂

潜在的不安全 HTML 需要使用 Angulars DOM 消毒剂明确标记为可信,因此不会删除内容的潜在不安全部分

<div [innerHTML]="myVal | safeHtml"></div>

这样的管道
@Pipe({name: 'safeHtml'})
export class Safe {
  constructor(private sanitizer:DomSanitizer){}

  transform(style) {
    return this.sanitizer.bypassSecurityTrustHtml(style);
    //return this.sanitizer.bypassSecurityTrustStyle(style);
    // return this.sanitizer.bypassSecurityTrustXxx(style); - see docs
  }
}

另见

和文档:https://angular.io/api/platform-browser/DomSanitizer

安全警告

已添加信任用户 HTML 可能会带来安全风险。 mentioned docs 之前的状态:

Calling any of the bypassSecurityTrust... APIs disables Angular's built-in sanitization for the value passed in. Carefully check and audit all values and code paths going into this call. Make sure any user data is appropriately escaped for this security context. For more detail, see the Security Guide.

Angular 标记

类似

class FooComponent {
  bar = 'bar';
  foo = `<div>{{bar}}</div>
    <my-comp></my-comp>
    <input [(ngModel)]="bar">`;

<div [innerHTML]="foo"></div>

不会导致 Angular 处理任何 Angular-specific in foo。 Angular 在构建时用生成的代码替换 Angular 特定标记。在运行时添加的标记 不会被 Angular 处理。

要添加包含 Angular 特定标记(属性 或值绑定、组件、指令、管道...)的 HTML,需要添加动态模块并在运行时编译组件。 此答案提供了更多详细信息

如果 Angular 的 DOM 清理器包含用户创建的内容,则直接使用 [innerHTML] 不是一个选项。 @GünterZöchbauer 建议的 safeHtml 管道是清理内容的一种方法。以下指令是另一个指令:

import { Directive, ElementRef, Input, OnChanges, Sanitizer, SecurityContext,
  SimpleChanges } from '@angular/core';

// Sets the element's innerHTML to a sanitized version of [safeHtml]
@Directive({ selector: '[safeHtml]' })
export class HtmlDirective implements OnChanges {
  @Input() safeHtml: string;

  constructor(private elementRef: ElementRef, private sanitizer: Sanitizer) {}

  ngOnChanges(changes: SimpleChanges): any {
    if ('safeHtml' in changes) {
      this.elementRef.nativeElement.innerHTML =
        this.sanitizer.sanitize(SecurityContext.HTML, this.safeHtml);
    }
  }
}

待用

<div [safeHtml]="myVal"></div>

Angular 2中你可以做3种类型的绑定:

  • [property]="expression" -> 任何 html 属性 都可以 link 到
    表达。在这种情况下,如果表达式更改 属性 将更新, 但反过来就不行了。
  • (event)="expression" -> 当事件激活时执行表达式。
  • [(ngModel)]="property" -> 将 js(或 ts)的 属性 绑定到 html。 属性 的任何更新都会随处可见。

表达式可以是值、属性或方法。例如:'4', 'controller.var', 'getValue()'

示例here

只需在 HTML 中使用 [innerHTML] 属性,如下所示:

<div [innerHTML]="myVal"></div>

Ever had properties in your component that contain some html markup or entities that you need to display in your template? The traditional interpolation won't work, but the innerHTML property binding comes to the rescue.

使用{{myVal}}没有按预期工作! 不会 获取 HTML 标签,如 <p><strong> 等,并且仅将其作为字符串传递...

假设您的组件中有此代码:

const myVal:string ='<strong>Whosebug</strong> is <em>helpful!</em>'

如果您使用 {{myVal}},您将在视图中看到:

<strong>Whosebug</strong> is <em>helpful!</em>

但使用 [innerHTML]="myVal" 使结果符合预期,如下所示:

Whosebug 很有帮助!

如 Angular 2 文档所述,动态添加元素到 DOM 的方法是使用来自 @Angular/core.[=14= 的 ViewContainerRef class ]

您需要做的是声明一个指令,该指令将实现 ViewContainerRef 并充当 DOM 上的占位符。

指令

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appInject]'
})
export class InjectDirective {

  constructor(public viewContainerRef: ViewContainerRef) { }

}

然后,在要注入组件的模板中:

HTML

<div class="where_you_want_to_inject">    
  <ng-template appInject></ng-template>
</div>

然后,从注入的组件代码中,您将注入包含您想要的 HTML 的组件:

import { Component, OnInit, ViewChild, ComponentFactoryResolver } from '@angular/core';
import { InjectDirective } from '../inject.directive';
import { InjectedComponent } from '../injected/injected.component';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {

  @ViewChild(InjectDirective) injectComp: InjectDirective;

  constructor(private _componentFactoryResolver: ComponentFactoryResolver) {
  }

  ngOnInit() {
  }

  public addComp() {
    const componentFactory = this._componentFactoryResolver.resolveComponentFactory(InjectedComponent);
    const viewContainerRef = this.injectComp.viewContainerRef;
    const componentRef = viewContainerRef.createComponent(componentFactory);
  }

  public removeComp() {
    const componentFactory = this._componentFactoryResolver.resolveComponentFactory(InjectedComponent);
    const viewContainerRef = this.injectComp.viewContainerRef;
    const componentRef = viewContainerRef.remove();
  }

}

我在 Angular 2 dynamically add component to DOM demo

上添加了一个完整的演示应用程序

如果您的 angular(或任何框架)应用程序中有模板,并且您通过 HTTP request/response 来自后端的 return HTML 模板,那么您是混淆了前端和后端之间的模板。

为什么不把模板的东西留在前端(我建议这样做),或者留在后端(我觉得相当不透明)?

如果您在前端保留模板,为什么不直接响应 JSON 以响应对后端的请求。您甚至不必实现 RESTful 结构,但将模板保留在一侧会使您的代码更加透明。

当其他人不得不处理您的代码时(或者甚至您自己在一段时间后重新输入自己的代码),这将得到回报!

如果操作正确,您将拥有带有小模板的小组件,最重要的是,如果您的代码是 imba,不懂编码语言的人将能够理解您的模板和您的逻辑!因此,另外,请尽可能小地保持 functions/methods。 您最终会发现,与大型 functions/methods/类 以及混合前端和后端之间的模板和逻辑相比,维护、重构、审查和添加功能会容易得多 - 并且保持尽可能多如果您的前端需要更灵活(例如,编写 android 前端或切换到不同的前端框架),请考虑后端逻辑。

哲学,伙计:)

p.s.: 你不必实施 100% 干净的代码,因为它非常昂贵 - 特别是如果你必须激励团队成员;) 但是:你应该在更简洁的代码方法和你拥有的代码之间找到一个很好的平衡(也许它已经很干净了)

如果可以的话,看看书,让它进入你的灵魂: https://de.wikipedia.org/wiki/Clean_Code

您可以使用多种方法来实现解决方案。正如已批准的答案中所说,您可以使用:

<div [innerHTML]="myVal"></div>

根据你想要实现的目标,你也可以尝试其他的东西,比如javascript DOM(不推荐,DOM操作很慢):

演示文稿

<div id="test"></test>

组件

var p = document.getElementsById("test");
p.outerHTML = myVal;

Property Binding

Javascript DOM Outer HTML

我们总是可以将 html 内容传递给 innerHTML 属性 以呈现 html 动态内容,但动态 html 内容也可能被感染或恶意.因此,在将动态内容传递给 innerHTML 之前,我们应该始终确保内容经过清理(使用 DOMSanitizer),以便我们可以转义所有恶意内容。

试试下面的管道:

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

@Pipe({name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
    constructor(private sanitized: DomSanitizer) {
    }
    transform(value: string) {
        return this.sanitized.bypassSecurityTrustHtml(value);
    }
}

Usage:
<div [innerHTML]="content | safeHtml"></div>

如果你想在 Angular 2 或 Angular 4 中保持内联 CSS 那么你可以使用

<div [innerHTML]="theHtmlString | keepHtml"></div>

您可以为样式应用多个管道,link 和 HTML,如下所示。html

<div [innerHTML]="announcementContent | safeUrl| safeHtml">
                    </div>

并在 'URL' 消毒剂的 .ts 管道中

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

@Pipe({ name: 'safeUrl' })
export class SafeUrlPipe implements PipeTransform {
    constructor(private sanitizer: DomSanitizer) {}
    transform(url) {
        return this.sanitizer.bypassSecurityTrustResourceUrl(url);
    }
}

用于 'HTML' 消毒剂的管道

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

@Pipe({
    name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
    constructor(private sanitized: DomSanitizer) {}
    transform(value) {
        return this.sanitized.bypassSecurityTrustHtml(value);
    }
}

这将在不影响任何样式和 link 单击事件的情况下应用

此处已提供简答:使用 <div [innerHTML]="yourHtml"> 绑定。

然而,此处提到的其他建议可能具有误导性。 Angular 当你绑定到这样的属性时,有一个内置的清理机制。由于 Angular 不是专门的清理库,因此对可疑内容过于热心,不冒任何风险。例如,它将所有 SVG 内容清理为空字符串。

您可能会听到 "sanitize" 您的内容的建议,方法是使用 DomSanitizer 通过 bypassSecurityTrustXXX 方法将内容标记为安全。还有一些建议使用管道来做到这一点,并且该管道通常称为 safeHtml.

所有这些都具有误导性,因为它实际上绕过清理,而不是清理您的内容。这可能是一个安全问题,因为如果您对用户提供的内容或您不确定的任何内容执行此操作,您就会面临恶意代码攻击。

如果 Angular 通过其内置清理删除了您需要的内容 — 您可以做的不是禁用它,而是将实际清理委托给擅长该任务的专用库。例如——DOMPurify.

我为它制作了一个包装器库,因此它可以很容易地与 Angular 一起使用: https://github.com/TinkoffCreditSystems/ng-dompurify

它还有一个管道用于声明性清理 HTML:

<div [innerHtml]="value | dompurify"></div>

此处建议的管道的不同之处在于它实际上确实通过 DOMPurify 进行了清理,因此适用于 SVG。

要记住的一件事是 DOMPurify 非常适合消毒 HTML/SVG,但不是 CSS。因此,您可以提供 Angular 的 CSS 消毒剂来处理 CSS:

import {NgModule, ɵ_sanitizeStyle} from '@angular/core';
import {SANITIZE_STYLE} from '@tinkoff/ng-dompurify';

@NgModule({
    // ...
    providers: [
        {
            provide: SANITIZE_STYLE,
            useValue: ɵ_sanitizeStyle,
        },
    ],
    // ...
})
export class AppModule {}

它是内部的 — hense ɵ 前缀,但这也是 Angular 团队在他们自己的包中使用它的方式。该库也适用于 Angular 通用和服务器端更新环境。

 <div [innerHTML]="HtmlPrint"></div><br>

innerHtml 是 属性 个 HTML 元素,它允许您以编程方式设置它的 html 内容。还有一个innerText属性定义内容为纯文本

属性周围的 [attributeName]="value" 框括号定义了一个 Angular 输入绑定。这意味着,属性 的值(在您的例子中是 innerHtml)绑定到给定的表达式,当表达式结果更改时,属性 值也会更改。

所以基本上 [innerHtml] 允许您绑定和动态更改给定 HTML-元素的 html-内容。

您还可以使用 DOM 属性 绑定 .[=14 将 angular 组件 class 属性与模板 绑定=]

示例:<div [innerHTML]="theHtmlString"></div>

使用如下规范形式:

<div bind-innerHTML="theHtmlString"></div>

Angular 文档:https://angular.io/guide/template-syntax#property-binding-property

查看工作 stackblitz example here

只是 post 对目前所有优秀答案的一点补充:如果您正在使用 [innerHTML] 渲染 Angular 组件并且对它不能像我一样工作感到沮丧,看看我为解决这个问题而编写的 ngx-dynamic-hooks 库。

有了它,您可以从动态 strings/html 加载组件,而不会影响安全性 。它实际上也像 [innerHTML] 一样使用 Angular 的 DOMSanitizer,但保留了加载组件的能力(以安全的方式)。

查看实际效果 in this Stackblitz

您可以使用以下两种方式。

<div [innerHTML]="myVal"></div>

<div innerHTML="{{myVal}}"></div>

Angular 2+ 支持将呈现 HTML 的 [innerHTML] 属性 绑定。如果您要以其他方式使用插值,它将被视为字符串。

进入.html文件

<div [innerHTML]="theHtmlString"></div>

进入.ts文件

theHtmlString:String = "enter your html codes here";

我已经构建了以下库,这将有助于重新绑定 html 格式的绑定。 请在下面找到使用该库的步骤。这个库基本上允许在 AOT

中注入 JIT 编译器代码
  1. 使用

    安装库
    npm i angular-html-recompile
    
  2. 在app.component.html文件中添加以下代码

    <pk-angular-html-recompile *ngIf="template !== ''" 
                               [stringTemplate]="template" 
                               [data]="dataObject"> 
    </pk-angular-html-recompile>
    
  3. 在 app.component.ts 文件中使用以下代码

    import { Component, OnInit, ViewChild } from '@angular/core';
    import { AngularHtmlRecompileComponent, AngularHtmlRecompileService } from 'angular-html-recompile';
    
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.scss']
    })
    export class AppComponent implements OnInit {
    
        @ViewChild(AngularHtmlRecompileComponent, { static: true }) comp !: AngularHtmlRecompileComponent;
    
        constructor(
            private angularHtmlRecompileService: AngularHtmlRecompileService) {
        }
    
        public dataObject: any;
    
        public template = `<div class="login-wrapper" fxLayout="row" fxLayoutAlign="center center">
        <mat-card class="box">
            <mat-card-header>
            <mat-card-title>Register</mat-card-title>
            </mat-card-header>
            <form class="example-form">
            <mat-card-content>
                <mat-form-field class="example-full-width">
                <input matInput placeholder="Username" [value]="Username" (keydown)="onControlEvent($event,'Username')">
                </mat-form-field>
                <mat-form-field class="example-full-width">
                <input matInput placeholder="Email" [value]="Email" (keydown)="onControlEvent($event,'Email')">
                </mat-form-field>
                <mat-form-field *ngIf="isShow" class="example-full-width">
                <input matInput placeholder="Password" [value]="Password" (keydown)="onControlEvent($event,'Password')">
                </mat-form-field>
                <mat-form-field class="example-full-width">
                <mat-label>Choose a role...</mat-label>
                <mat-select (selectionChange)="onControlEvent($event, 'selectedValue')">
                    <mat-option [value]="roles" *ngFor="let roles of Roles">{{roles}}
                    </mat-option>
                </mat-select>
                </mat-form-field>
            </mat-card-content>
            <button mat-stroked-button color="accent" class="btn-block" (click)="buttomClickEvent('submit')" >Register</button>
            </form>
        </mat-card>
        </div>`;
    
        ngOnInit(): void {
    
            this.angularHtmlRecompileService.sharedData.subscribe((respose: any) => {
                if (respose) {
                    switch (respose.key) {
                        case `Username`:
                            // Call any method on change of name
                            break;
                        case `Password`:
                            //Update password from main component
                            this.comp[`cmpRef`].instance['Password'] = "Karthik";
                            break;
                        case `submit`:
                            //Get reference of all parameters on submit click
                            //1. respose.data OR
                            //use this.comp[`cmpRef`].instance
                            break;
                        default:
                            break;
                    }
                }
            });
    
            this.prepareData();
    
        }
    
        prepareData() {
    
            //Prepare data in following format only for easy binding
            //Template preparation and data preparation can be done once data received from service
    
            // AngularHtmlRecompileComponent will not be rendered until you pass data 
    
            this.dataObject = [
                { key: 'Username', value: 'Pranay' },
                { key: 'Email', value: 'abc@test.com', },
                { key: 'Password', value: 'test123', },
                { key: 'Roles', value: ['Admin', 'Author', 'Reader'] },
                { key: 'isShow', value: this.updateView() }
            ];
    
        }
    
        updateView() {
            //Write down logic before rendering to UI to work ngIf directive
            return true;
        }
    }
    
  4. 将模块添加到 app.module.ts 文件

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    
    import { AppComponent } from './app.component';
    import { AngularHtmlRecompileModule } from "angular-html-recompile";
    
    @NgModule({
        declarations: [
            AppComponent
        ],
        imports: [
            BrowserModule,
            AngularHtmlRecompileModule
        ],
        providers: [],
        bootstrap: [AppComponent]
    })
    export class AppModule { }
    
  5. 此库支持基本的 html、Angular material、弹性布局。 要使用此功能,请安装以下依赖项

    npm i -s @angular/material @angular/flex-layout