我正在编写一个 Angular 应用程序,并且我想要显示一个 HTML 响应。
我怎么做?如果我只是使用绑定语法{{myVal}}
,它会编码所有 HTML 字符(当然)。
我需要以某种方式将innerHTML
a绑定div
到变量值。
我正在编写一个 Angular 应用程序,并且我想要显示一个 HTML 响应。
我怎么做?如果我只是使用绑定语法{{myVal}}
,它会编码所有 HTML 字符(当然)。
我需要以某种方式将innerHTML
a绑定div
到变量值。
Angular 2.0.0 和 Angular 4.0.0 最终版
只是为了安全的内容
<div [innerHTML]="myVal"></div>
DOMSanitizer
潜在的不安全 HTML 需要使用 Angulars DOM sanitizer 明确标记为受信任,因此不会剥离内容的潜在不安全部分
<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 可能会带来安全风险。前面提到的文档状态:
调用任何
bypassSecurityTrust...
API 都会禁用 Angular 对传入值的内置清理。仔细检查和审核进入此调用的所有值和代码路径。确保针对此安全上下文适当地转义了任何用户数据。有关更多详细信息,请参阅安全指南。
角度标记
就像是
class FooComponent {
bar = 'bar';
foo = `<div>{{bar}}</div>
<my-comp></my-comp>
<input [(ngModel)]="bar">`;
和
<div [innerHTML]="foo"></div>
不会导致 Angular 处理foo
. Angular 在构建时用生成的代码替换了 Angular 特定的标记。Angular 不会处理在运行时添加的标记。
要添加包含 Angular 特定标记(属性或值绑定、组件、指令、管道……)的 HTML,需要添加动态模块并在运行时编译组件。此答案提供了更多详细信息如何使用/创建动态模板来使用 Angular 2.0 编译动态组件?
[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 元素。
这只是一种使用本机 javascript 的方式,但在 Angular 环境中。我不推荐它,因为会使代码更混乱,但有时没有其他选择。
在 angular2@2.0.0-alpha.44 上:
使用 时,Html-Binding 将不起作用{{interpolation}}
,请改用“表达式”:
无效的
<p [innerHTML]="{{item.anleser}}"></p>
-> 抛出错误(插值而不是预期的表达式)
正确的
<p [innerHTML]="item.anleser"></p>
-> 这是正确的方法。
您可以在表达式中添加其他元素,例如:
<p [innerHTML]="'<b>'+item.anleser+'</b>'"></p>
暗示
除了出于安全目的的清理之外,Angular 不会以任何方式处理使用[innerHTML]
(或通过其他类似或类似方式动态添加)添加的 HTML 。
只有在将 HTML 静态添加到组件模板时,这些事情才会起作用。如果您需要这个,您可以在运行时创建一个组件,如我如何使用/创建动态模板以使用 Angular 2.0 编译动态组件?element.appenChild()
如果 [innerHTML] 包含用户创建的内容,则不能直接使用 [innerHTML] 而不使用 Angular 的 DOM sanitizer。@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>
请参阅其他更新的答案。
这对我有用:<div innerHTML = "{{ myVal }}"></div>
(Angular2,Alpha 33)
根据另一个 SO: Inserting HTML from server into DOM with angular2 (Angular2 中的一般 DOM 操作),“inner-html”相当于 Angular 1.X 中的“ng-bind-html”
只是为了获得完整的答案,如果您的HTML
内容在 a 中component
variable
,您还可以使用:
<div [innerHTML]=componentVariableThatHasTheHtml></div>
如果我在这里遗漏了要点,我深表歉意,但我想推荐一种不同的方法:
我认为最好从服务器端应用程序返回原始数据并将其绑定到客户端的模板。这使得请求更加灵活,因为您只从服务器返回 json。
对我来说,如果您所做的只是从服务器获取 html 并将其“按原样”注入 DOM,那么使用 Angular 似乎没有意义。
我知道 Angular 1.x 有一个 html 绑定,但我还没有看到 Angular 2.0 中的对应项。不过他们可能会在以后添加它。无论如何,我仍然会考虑为您的 Angular 2.0 应用程序提供数据 api。
如果您有兴趣,我这里有一些带有一些简单数据绑定的示例:http ://www.syntaxsuccess.com/viewarticle/angular-2.0-examples
这里已经提供了简短的答案:使用<div [innerHTML]="yourHtml">
绑定。
然而,这里提到的其他建议可能具有误导性。当你绑定到这样的属性时,Angular 有一个内置的清理机制。由于 Angular 不是一个专门的清理库,因此对可疑内容过于热衷而不冒任何风险。例如,它将所有 SVG 内容清理为空字符串。
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 sanitizer 来处理 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 Universal 和服务器端渲染环境。
只需在HTML[innerHTML]
中使用属性,如下所示:
<div [innerHTML]="myVal"></div>
您的组件中是否曾经有包含一些 html 标记或需要在模板中显示的实体的属性?传统的插值不起作用,但 innerHTML 属性绑定来拯救。
使用{{myVal}}
不按预期工作!这不会拾取 HTML 标签,如<p>
,<strong>
等,并仅将其作为字符串传递......
想象一下,您的组件中有以下代码:
const myVal:string ='<strong>Stackoverflow</strong> is <em>helpful!</em>'
如果您使用{{myVal}}
,您将在视图中看到:
<strong>Stackoverflow</strong> is <em>helpful!</em>
但是使用[innerHTML]="myVal"
使结果符合预期,如下所示:
Stackoverflow很有帮助!
<div [innerHTML]="HtmlPrint"></div><br>
innerHtml是 HTML-Elements 的一个属性,它允许您以编程方式设置它的 html 内容。还有一个 innerText 属性将内容定义为纯文本。
包围属性的[attributeName]="value"
方括号定义了一个 Angular 输入绑定。这意味着,属性的值(在您的情况下为 innerHtml)绑定到给定的表达式,当表达式结果发生变化时,属性值也会发生变化。
所以基本上[innerHtml]
允许您绑定和动态更改给定 HTML 元素的 html 内容。
您可以在 .html 中为样式、链接和 HTML 应用多个管道,如下所示
<div [innerHTML]="announcementContent | safeUrl| safeHtml">
</div>
并在 .ts 管道中用于“URL”消毒剂
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);
}
}
这将同时适用而不会干扰任何样式和链接点击事件
在Angular 2中,您可以进行 3 种类型的绑定:
[property]="expression"
-> 任何 html 属性都可以链接到(event)="expression"
-> 当事件激活执行表达式时。[(ngModel)]="property"
-> 将属性从 js(或 ts)绑定到 html。此属性的任何更新都会随处可见。表达式可以是值、属性或方法。例如:'4'、'controller.var'、'getValue()'
这里的例子
您还可以使用 DOM 属性绑定将角度组件类属性与模板绑定。
例子:<div [innerHTML]="theHtmlString"></div>
使用如下规范形式:
<div bind-innerHTML="theHtmlString"></div>
角度文档:https ://angular.io/guide/template-syntax#property-binding-property
请参阅此处的工作 stackblitz示例
我们总是可以将 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>
您可以使用以下两种方式。
<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 编译器代码
使用安装库
npm i angular-html-recompile
在 app.component.html 文件中添加以下代码
<pk-angular-html-recompile *ngIf="template !== ''"
[stringTemplate]="template"
[data]="dataObject">
</pk-angular-html-recompile>
在 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;
}
}
将模块添加到 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 { }
这个库支持基本的 html、Angular 材质、flex 布局。要使用此功能,请安装以下依赖项
npm i -s @angular/material @angular/flex-layout
在 Angular v2.1.1 中工作
<div [innerHTML]="variable or htmlString">
</div>
如 Angular 2 文档所述,动态添加元素到 DOM 的方法是使用 @Angular/core 中的 ViewContainerRef 类。
您需要做的是声明一个指令,该指令将实现 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上添加了一个完整工作的演示应用程序,将组件动态添加到 DOM 演示
您可以使用多种方法来实现解决方案。如已批准的答案中所述,您可以使用:
<div [innerHTML]="myVal"></div>
根据您要实现的目标,您还可以尝试其他东西,例如 javascript DOM(不推荐,DOM 操作很慢):
介绍
<div id="test"></test>
零件
var p = document.getElementsById("test");
p.outerHTML = myVal;
如果您想在 Angular 2 或 Angular 4 中使用它并且还想保留内联 CSS,那么您可以使用
<div [innerHTML]="theHtmlString | keepHtml"></div>
只是对到目前为止所有的好答案发表一点补充:如果您正在使用[innerHTML]
渲染 Angular 组件并且对它不像我一样工作感到沮丧,请查看我为解决这个问题而编写的ngx-dynamic-hooks库问题。
有了它,您可以从动态字符串/html 加载组件,而不会影响安全性。它实际上也像使用 AngularDOMSanitizer
一样[innerHTML]
,但保留了加载组件的能力(以安全的方式)。
在此 Stackblitz 中查看它的实际效果。
如果您的 Angular(或任何框架)应用程序中有模板,并且您通过 HTTP 请求/响应从后端返回 HTML 模板,那么您正在混淆前端和后端之间的模板。
为什么不将模板内容留在前端(我建议)或后端(非常不透明的 imo)?
如果您将模板保留在前端,为什么不使用 JSON 响应后端请求。您甚至不必实现 RESTful 结构,但将模板保留在一侧会使您的代码更加透明。
当其他人不得不处理您的代码(或者甚至您自己在一段时间后重新输入自己的代码)时,这将得到回报!
如果你做得对,你将拥有带有小模板的小组件,最重要的是,如果你的代码是 imba,那么不懂编码语言的人将能够理解你的模板和逻辑!因此,另外,尽可能地保持你的函数/方法小。您最终会发现,与大型函数/方法/类以及在前端和后端之间混合模板和逻辑相比,维护、重构、审查和添加功能要容易得多 - 并将尽可能多的逻辑保留在后端如果您的前端需要更灵活(例如编写一个 android 前端或切换到不同的前端框架)。
哲学,男人:)
ps:您不必实现 100% 干净的代码,因为它非常昂贵 - 特别是如果您必须激励团队成员;)但是:您应该在清洁代码的方法和您拥有的东西之间找到一个很好的平衡(也许它已经很干净了)
如果可以的话,检查一下这本书,让它进入你的灵魂: https ://de.wikipedia.org/wiki/Clean_Code