等价于角度2中的编译

我想手动编译一些 HTML 包含的指令。什么是等效的 $compile在角度2?

例如,在 Angular 1中,我可以动态编译一个 HTML 片段并将其附加到 DOM:

var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
75407 次浏览

如果你想注入 html 代码使用指令

<div [innerHtml]="htmlVar"></div>

如果您想在某个地方加载整个组件,请使用 DynamicComponent entLoader:

Https://angular.io/docs/ts/latest/api/core/dynamiccomponentloader-class.html

注意: 正如@BennyBottema 在注释中提到的,DynamicComponent 加载器现在不被推荐使用,因此这个答案也不被推荐使用。


Angular2没有任何 $编译等价物。您可以使用 DynamicComoponentLoader并使用 ES6类来动态编译代码(参见此 噗通) :

import {Component, DynamicComponentLoader, ElementRef, OnInit} from 'angular2/core'


function compileToComponent(template, directives) {
@Component({
selector: 'fake',
template , directives
})
class FakeComponent {};
return FakeComponent;
}


@Component({
selector: 'hello',
template: '<h1>Hello, Angular!</h1>'
})
class Hello {}


@Component({
selector: 'my-app',
template: '<div #container></div>',
})
export class App implements OnInit {
constructor(
private loader: DynamicComponentLoader,
private elementRef: ElementRef,
) {}


ngOnInit() {} {
const someDynamicHtml = `<hello></hello><h2>${Date.now()}</h2>`;


this.loader.loadIntoLocation(
compileToComponent(someDynamicHtml, [Hello])
this.elementRef,
'container'
);
}
}

但是它只能在 html 解析器进入 angular2核心之前工作。

角度 2.3.0(2016-12-07)

检查所有细节:

为了实现这一目标:

主要内容:

1)建立模板
2)创建组件
3)创建模块
4)编译模块
5)创建(并缓存)组件工厂
6)使用 Target 创建它的实例

如何创建组件的快速概述

createNewComponent (tmpl:string) {
@Component({
selector: 'dynamic-component',
template: tmpl,
})
class CustomDynamicComponent  implements IHaveDynamicData {
@Input()  public entity: any;
};
// a component for this particular template
return CustomDynamicComponent;
}

一种在 NgModule 中注入组件的方法

createComponentModule (componentType: any) {
@NgModule({
imports: [
PartsModule, // there are 'text-editor', 'string-editor'...
],
declarations: [
componentType
],
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}

如何创建 ComponentFactory (并缓存它)的代码片段

public createComponentFactory(template: string)
: Promise<ComponentFactory<IHaveDynamicData>> {
let factory = this._cacheOfFactories[template];


if (factory) {
console.log("Module and Type are returned from cache")


return new Promise((resolve) => {
resolve(factory);
});
}


// unknown template ... let's create a Type for it
let type   = this.createNewComponent(template);
let module = this.createComponentModule(type);


return new Promise((resolve) => {
this.compiler
.compileModuleAndAllComponentsAsync(module)
.then((moduleWithFactories) =>
{
factory = _.find(moduleWithFactories.componentFactories
, { componentType: type });


this._cacheOfFactories[template] = factory;


resolve(factory);
});
});
}

如何使用上述结果的代码片段

  // here we get Factory (just compiled or from cache)
this.typeBuilder
.createComponentFactory(template)
.then((factory: ComponentFactory<IHaveDynamicData>) =>
{
// Target will instantiate and inject component (we'll keep reference to it)
this.componentRef = this
.dynamicComponentTarget
.createComponent(factory);


// let's inject @Inputs to component instance
let component = this.componentRef.instance;


component.entity = this.entity;
//...
});

完整的描述与所有的细节 看这里,或观察 工作范例

.

.

过时-角度2.0 RC5相关(只有 RC5)

查看以前的 RC 版本的解决方案,请通过 这个职位的历史搜索

为了动态地创建一个组件的实例并将其附加到 DOM,您可以使用以下脚本,并且应该在 角钢筋混凝土中工作:

Html 范本:

<div>
<div id="container"></div>
<button (click)="viewMeteo()">Meteo</button>
<button (click)="viewStats()">Stats</button>
</div>

装入器组件

import { Component, DynamicComponentLoader, ElementRef, Injector } from '@angular/core';
import { WidgetMeteoComponent } from './widget-meteo';
import { WidgetStatComponent } from './widget-stat';


@Component({
moduleId: module.id,
selector: 'widget-loader',
templateUrl: 'widget-loader.html',
})
export class WidgetLoaderComponent  {


constructor( elementRef: ElementRef,
public dcl:DynamicComponentLoader,
public injector: Injector) { }


viewMeteo() {
this.dcl.loadAsRoot(WidgetMeteoComponent, '#container', this.injector);
}


viewStats() {
this.dcl.loadAsRoot(WidgetStatComponent, '#container', this.injector);
}


}

角形字体/ES6(角形2 +)

同时使用 AOT + JIT。

我创造了如何在这里使用它: Https://github.com/patrikx3/angular-compile

npm install p3x-angular-compile

组件: 应该有一个上下文和一些 html 数据..。

网址:

<div [p3x-compile]="data" [p3x-compile-context]="ctx">loading ...</div>

角版本我已经使用-角度4.2.0

角度4提出了 组件工厂解析器在运行时加载组件。这是一种相同的 $编译在角1.0的实现,它满足你的需要

在下面的示例中,我将 ImageWidget组件动态加载到 DashboardTileComponent

为了加载一个组件,您需要一个指令,您可以应用到 模板,这将有助于放置动态组件

WidgetHostDirective

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


@Directive({
selector: '[widget-host]',
})
export class DashboardTileWidgetHostDirective {
constructor(public viewContainerRef: ViewContainerRef) {




}
}

此指令注入 ViewContainerRef以获得对将承载动态添加的组件的元素的视图容器的访问。

DashboardTileComponent (放置 holder 组件以呈现动态组件)

此组件接受来自父组件的输入,或者您可以根据实现从服务加载。该组件在运行时扮演解析组件的主要角色。在这个方法中,您还可以看到一个名为 RenderComponent ()的方法,它最终从服务加载组件名称,并用 组件工厂解析器解析,最终将数据设置为动态组件。

import { Component, Input, OnInit, AfterViewInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
import { DashboardTileWidgetHostDirective } from './DashbardWidgetHost.Directive';
import { TileModel } from './Tile.Model';
import { WidgetComponentService } from "./WidgetComponent.Service";




@Component({
selector: 'dashboard-tile',
templateUrl: 'app/tile/DashboardTile.Template.html'
})


export class DashboardTileComponent implements OnInit {
@Input() tile: any;
@ViewChild(DashboardTileWidgetHostDirective) widgetHost: DashboardTileWidgetHostDirective;
constructor(private _componentFactoryResolver: ComponentFactoryResolver,private widgetComponentService:WidgetComponentService) {


}


ngOnInit() {


}
ngAfterViewInit() {
this.renderComponents();
}
renderComponents() {
let component=this.widgetComponentService.getComponent(this.tile.componentName);
let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
let viewContainerRef = this.widgetHost.viewContainerRef;
let componentRef = viewContainerRef.createComponent(componentFactory);
(<TileModel>componentRef.instance).data = this.tile;


}
}

DashboardTileComponent. html

 <div class="col-md-2 col-lg-2 col-sm-2 col-default-margin col-default">
<ng-template widget-host></ng-template>


</div>

WidgetComponent 服务

这是一个服务工厂,用于注册要动态解析的所有组件

import { Injectable }           from '@angular/core';
import { ImageTextWidgetComponent } from "../templates/ImageTextWidget.Component";
@Injectable()
export class WidgetComponentService {
getComponent(componentName:string) {
if(componentName==="ImageTextWidgetComponent"){
return ImageTextWidgetComponent
}
}
}

ImageTextWidgetComponent (我们在运行时加载的组件)

import { Component, OnInit, Input } from '@angular/core';




@Component({
selector: 'dashboard-imagetextwidget',
templateUrl: 'app/templates/ImageTextWidget.html'
})


export class ImageTextWidgetComponent implements OnInit {
@Input() data: any;
constructor() { }


ngOnInit() { }
}

最后,将这个 ImageTextWidgetComponent 作为 entryComponent 添加到应用程序模块中

@NgModule({
imports: [BrowserModule],
providers: [WidgetComponentService],
declarations: [
MainApplicationComponent,
DashboardHostComponent,
DashboardGroupComponent,
DashboardTileComponent,
DashboardTileWidgetHostDirective,
ImageTextWidgetComponent
],
exports: [],
entryComponents: [ImageTextWidgetComponent],
bootstrap: [MainApplicationComponent]
})
export class DashboardModule {
constructor() {


}
}

瓷砖模型

 export interface TileModel {
data: any;
}

原始参考我的博客

正式文件

下载示例源代码

这个 npm 软件包使我更容易: Https://www.npmjs.com/package/ngx-dynamic-template

用途:

<ng-template dynamic-template
[template]="'some value:\{\{param1}}, and some component <lazy-component></lazy-component>'"
[context]="{param1:'value1'}"
[extraModules]="[someDynamicModule]"></ng-template>

我知道这个问题已经很久了,但是我花了几个星期的时间试图弄清楚如何在启用 AOT 的情况下实现这个功能。我能够编译一个对象,但从来不能执行现有的组件。我最终决定改变策略,因为我不想编译代码,而是执行一个自定义模板。我的想法是添加 HTML,任何人都可以这样做,并循环通过现有的工厂。在这样做时,我可以搜索元素/属性/等名称,并在该 HTMLElement 上执行组件。我能够让它工作,并认为我应该分享这一点,以节省别人大量的时间,我浪费在它上面。

@Component({
selector: "compile",
template: "",
inputs: ["html"]
})
export class CompileHtmlComponent implements OnDestroy {
constructor(
private content: ViewContainerRef,
private injector: Injector,
private ngModRef: NgModuleRef<any>
) { }


ngOnDestroy() {
this.DestroyComponents();
}


private _ComponentRefCollection: any[] = null;
private _Html: string;


get Html(): string {
return this._Html;
}
@Input("html") set Html(val: string) {
// recompile when the html value is set
this._Html = (val || "") + "";
this.TemplateHTMLCompile(this._Html);
}


private DestroyComponents() { // we need to remove the components we compiled
if (this._ComponentRefCollection) {
this._ComponentRefCollection.forEach((c) => {
c.destroy();
});
}
this._ComponentRefCollection = new Array();
}


private TemplateHTMLCompile(html) {
this.DestroyComponents();
this.content.element.nativeElement.innerHTML = html;
var ref = this.content.element.nativeElement;
var factories = (this.ngModRef.componentFactoryResolver as any)._factories;
// here we loop though the factories, find the element based on the selector
factories.forEach((comp: ComponentFactory<unknown>) => {
var list = ref.querySelectorAll(comp.selector);
list.forEach((item) => {
var parent = item.parentNode;
var next = item.nextSibling;
var ngContentNodes: any[][] = new Array(); // this is for the viewchild/viewchildren of this object


comp.ngContentSelectors.forEach((sel) => {
var ngContentList: any[] = new Array();


if (sel == "*") // all children;
{
item.childNodes.forEach((c) => {
ngContentList.push(c);
});
}
else {
var selList = item.querySelectorAll(sel);


selList.forEach((l) => {
ngContentList.push(l);
});
}


ngContentNodes.push(ngContentList);
});
// here is where we compile the factory based on the node we have
let component = comp.create(this.injector, ngContentNodes, item, this.ngModRef);


this._ComponentRefCollection.push(component); // save for our destroy call
// we need to move the newly compiled element, as it was appended to this components html
if (next) parent.insertBefore(component.location.nativeElement, next);
else parent.appendChild(component.location.nativeElement);


component.hostView.detectChanges(); // tell the component to detectchanges
});
});
}
}

可以看到这个组件,它允许编译简单的动态角度分量 https://www.npmjs.com/package/@codehint-ng/html-compiler