Rad*_*ler 185 compilation typescript angular2-compiler angular
我想动态创建模板.这应该用于ComponentType在运行时构建一个并在托管组件内部放置(甚至替换)它.
直到我使用RC4 ComponentResolver,但RC5我收到消息:
ComponentResolver不推荐用于动态编译.使用ComponentFactoryResolver连同@NgModule/@Component.entryComponents或ANALYZE_FOR_ENTRY_COMPONENTS提供商,而不是.对于仅运行时编译,您也可以使用Compiler.compileComponentSync/Async.
我发现了这个(官方的angular2)文件
并了解我可以使用其中之一
ngIf用ComponentFactoryResolver.如果我将已知组件传递到托管内部@Component({entryComponents: [comp1, comp2], ...})- 我可以使用.resolveComponentFactory(componentToRender);Compiler...但问题是如何使用它Compiler?上面的注释说我应该打电话:Compiler.compileComponentSync/Async- 那怎么样?
例如.我想为一种设置创建(基于一些配置条件)这种模板
<form>
<string-editor
[propertyName]="'code'"
[entity]="entity"
></string-editor>
<string-editor
[propertyName]="'description'"
[entity]="entity"
></string-editor>
...
Run Code Online (Sandbox Code Playgroud)
在另一种情况下这个(string-editor被替换text-editor)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
Run Code Online (Sandbox Code Playgroud)
等等(editors属性类型的不同数量/日期/引用,跳过一些用户的某些属性......).即这是一个例子,真正的配置可以生成更多不同和复杂的模板.
该模板是不断变化的,所以我不能使用ComponentFactoryResolver,并通过现有的...我需要的解决方案与Compiler
您是否希望将此功能与AOT一起使用(提前编译)?你得到了:
错误:静态解析符号值时遇到错误.不支持函数调用.考虑使用对导出函数的引用替换函数或lambda(原始.ts文件中的位置65:17),在.../node_modules/@angular/compiler/src/compiler.d.ts中解析符号COMPILER_PROVIDERS,
请留下您的评论,在这里投票:
Rad*_*ler 156
注意:要获得以前版本的解决方案,请查看此帖子的历史记录
这里讨论的类似主题相当于Angular 2中的$ compile.我们需要使用JitCompiler和NgModule.NgModule在这里阅读更多关于Angular2的内容:
有一个工作的plunker/example (动态模板,动态组件类型,动态模块JitCompiler,...在行动中)
主要是:
1)创建模板
2)ComponentFactory在缓存中查找- 转到7)
3) - 创建Component
4) - 创建Module
5) - 编译Module
6) - 返回(和缓存供以后使用)ComponentFactory
7)使用Target并ComponentFactory创建实例动态的Component
下面的代码片段(更多的是在这里) -我们的自定义生成器将返回刚刚建立/缓存ComponentFactory和视图目标占位消费创造的一个实例DynamicComponent
// here we get a TEMPLATE with dynamic content === TODO
var template = this.templateBuilder.prepareTemplate(this.entity, useTextarea);
// 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;
//...
});
Run Code Online (Sandbox Code Playgroud)
这就是它 - 简而言之.要了解更多详情,请阅读以下内容
.
观察一个plunker并回来阅读详细信息,以防一些代码片段需要更多解释
.
下面对这个场景的描述,我们会
PartsModule:NgModule (小块的持有者)DynamicModule:NgModule,它将包含我们的动态组件(和PartsModule动态引用)Component类型(仅当模板已更改时) RuntimeModule:NgModule.该模块将包含先前创建的Component类型JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)来ComponentFactory DynamicComponentView Target占位符和作业的实例ComponentFactory @Inputs给新实例 (从切换INPUT到TEXTAREA编辑),消耗@Outputs我们需要一个NgModule.
虽然我想展示一个非常简单的例子,在这种情况下,我需要三个模块(实际上是4个 - 但我不算AppModule).请把这个而不是一个简单的片段作为一个真正可靠的动态组件生成器的基础.
将有一个模块,所有的小部件,例如string-editor,text-editor (date-editor,number-editor...)
@NgModule({
imports: [
CommonModule,
FormsModule
],
declarations: [
DYNAMIC_DIRECTIVES
],
exports: [
DYNAMIC_DIRECTIVES,
CommonModule,
FormsModule
]
})
export class PartsModule { }
Run Code Online (Sandbox Code Playgroud)
哪些
DYNAMIC_DIRECTIVES是可扩展的,并且用于保存用于动态组件模板/类型的所有小部件.检查app/parts/parts.module.ts
第二个是我们动态物料处理的模块.它将包含托管组件和一些提供商..这将是单身人士.因此,我们将以标准方式发布它们forRoot()
import { DynamicDetail } from './detail.view';
import { DynamicTypeBuilder } from './type.builder';
import { DynamicTemplateBuilder } from './template.builder';
@NgModule({
imports: [ PartsModule ],
declarations: [ DynamicDetail ],
exports: [ DynamicDetail],
})
export class DynamicModule {
static forRoot()
{
return {
ngModule: DynamicModule,
providers: [ // singletons accross the whole app
DynamicTemplateBuilder,
DynamicTypeBuilder
],
};
}
}
Run Code Online (Sandbox Code Playgroud)
检查的使用
forRoot()中AppModule
最后,我们需要一个特殊的运行时模块..但是稍后将作为工作的一部分创建DynamicTypeBuilder.
第四个模块,应用程序模块,是保持声明编译器提供程序的人:
...
import { COMPILER_PROVIDERS } from '@angular/compiler';
import { AppComponent } from './app.component';
import { DynamicModule } from './dynamic/dynamic.module';
@NgModule({
imports: [
BrowserModule,
DynamicModule.forRoot() // singletons
],
declarations: [ AppComponent],
providers: [
COMPILER_PROVIDERS // this is an app singleton declaration
],
Run Code Online (Sandbox Code Playgroud)
在那里阅读(阅读)关于NgModule的更多信息:
在我们的示例中,我们将处理此类实体的详细信息
entity = {
code: "ABC123",
description: "A description of this Entity"
};
Run Code Online (Sandbox Code Playgroud)
要创建一个template,在这个plunker中我们使用这个简单/天真的构建器.
真正的解决方案,一个真正的模板构建器,是您的应用程序可以做很多事情的地方
// plunker - app/dynamic/template.builder.ts
import {Injectable} from "@angular/core";
@Injectable()
export class DynamicTemplateBuilder {
public prepareTemplate(entity: any, useTextarea: boolean){
let properties = Object.keys(entity);
let template = "<form >";
let editorName = useTextarea
? "text-editor"
: "string-editor";
properties.forEach((propertyName) =>{
template += `
<${editorName}
[propertyName]="'${propertyName}'"
[entity]="entity"
></${editorName}>`;
});
return template + "</form>";
}
}
Run Code Online (Sandbox Code Playgroud)
这里的一个技巧是 - 它构建一个使用一些已知属性的模板,例如entity.这样的属性(-ies)必须是动态组件的一部分,我们将在下面创建它.
为了使它更容易一些,我们可以使用接口来定义属性,我们的模板构建器可以使用它.这将由我们的动态组件类型实现.
export interface IHaveDynamicData {
public entity: any;
...
}
Run Code Online (Sandbox Code Playgroud)
ComponentFactory建设者这里非常重要的是要记住:
我们的组件类型,与我们一起构建
DynamicTypeBuilder,可能会有所不同 - 但仅限于其模板(在上面创建).组件的属性(输入,输出或某些受保护的)仍然相同.如果我们需要不同的属性,我们应该定义Template和Type Builder的不同组合
因此,我们正在触及我们解决方案的核心.构建器,将1)创建ComponentType2)创建其NgModule3)编译ComponentFactory4)缓存它以供以后重用.
我们需要获得的依赖:
// plunker - app/dynamic/type.builder.ts
import { JitCompiler } from '@angular/compiler';
@Injectable()
export class DynamicTypeBuilder {
// wee need Dynamic component builder
constructor(
protected compiler: JitCompiler
) {}
Run Code Online (Sandbox Code Playgroud)
这里有一个片段如何获得ComponentFactory:
// plunker - app/dynamic/type.builder.ts
// this object is singleton - so we can use this as a cache
private _cacheOfFactories:
{[templateKey: string]: ComponentFactory<IHaveDynamicData>} = {};
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);
});
});
}
Run Code Online (Sandbox Code Playgroud)
上面我们创建并缓存两者
Component和Module.因为如果模板(实际上是所有的真实动态部分)是相同的......我们可以重用
这里有两个方法,它们代表了如何在运行时创建装饰类/类型的非常酷的方法.不仅是,@Component而且@NgModule
protected 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;
}
protected createComponentModule (componentType: any) {
@NgModule({
imports: [
PartsModule, // there are 'text-editor', 'string-editor'...
],
declarations: [
componentType
],
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}
Run Code Online (Sandbox Code Playgroud)
重要:
我们的组件动态类型不同,但只是模板.所以我们使用这个事实来缓存它们.这非常重要.Angular2也将缓存由这些.. 类型.如果我们为相同的模板重新创建字符串新类型...我们将开始生成内存泄漏.
ComponentFactory 由托管组件使用最终作品是一个组件,它承载我们的动态组件的目标,例如<div #dynamicContentPlaceHolder></div>.我们得到它的引用并用于ComponentFactory创建组件.简而言之,这里是该组件的所有部分(如果需要,这里是开放的plunker)
我们首先总结一下导入语句:
import {Component, ComponentRef,ViewChild,ViewContainerRef} from '@angular/core';
import {AfterViewInit,OnInit,OnDestroy,OnChanges,SimpleChange} from '@angular/core';
import { IHaveDynamicData, DynamicTypeBuilder } from './type.builder';
import { DynamicTemplateBuilder } from './template.builder';
@Component({
selector: 'dynamic-detail',
template: `
<div>
check/uncheck to use INPUT vs TEXTAREA:
<input type="checkbox" #val (click)="refreshContent(val.checked)" /><hr />
<div #dynamicContentPlaceHolder></div> <hr />
entity: <pre>{{entity | json}}</pre>
</div>
`,
})
export class DynamicDetail implements AfterViewInit, OnChanges, OnDestroy, OnInit
{
// wee need Dynamic component builder
constructor(
protected typeBuilder: DynamicTypeBuilder,
protected templateBuilder: DynamicTemplateBuilder
) {}
...
Run Code Online (Sandbox Code Playgroud)
我们只接收模板和组件构建器.接下来是我们的示例所需的属性(更多注释)
// reference for a <div> with #dynamicContentPlaceHolder
@ViewChild('dynamicContentPlaceHolder', {read: ViewContainerRef})
protected dynamicComponentTarget: ViewContainerRef;
// this will be reference to dynamic content - to be able to destroy it
protected componentRef: ComponentRef<IHaveDynamicData>;
// until ngAfterViewInit, we cannot start (firstly) to process dynamic stuff
protected wasViewInitialized = false;
// example entity ... to be recieved from other app parts
// this is kind of candiate for @Input
protected entity = {
code: "ABC123",
description: "A description of this Entity"
};
Run Code Online (Sandbox Code Playgroud)
在这个简单的场景中,我们的托管组件没有任何@Input.所以它不必对变化做出反应.但尽管存在这一事实(并准备好即将发生的变化) - 如果组件已经(首先)已经启动,我们需要引入一些标志.只有这样我们才能开始魔术.
最后,我们将使用我们的组件构建器,它刚刚编译/缓存 ComponentFacotry.我们的目标占位符将被要求实例化该Component工厂.
protected refreshContent(useTextarea: boolean = false){
if (this.componentRef) {
this.componentRef.destroy();
}
// here we get a TEMPLATE with dynamic content === TODO
var template = this.templateBuilder.prepareTemplate(this.entity, useTextarea);
// 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;
//...
});
}
Run Code Online (Sandbox Code Playgroud)
此外,我们需要保持对已编译模板的引用..以便能够正确地进行destroy(),只要我们将更改它.
// this is the best moment where to start to process dynamic stuff
public ngAfterViewInit(): void
{
this.wasViewInitialized = true;
this.refreshContent();
}
// wasViewInitialized is an IMPORTANT switch
// when this component would have its own changing @Input()
// - then we have to wait till view is intialized - first OnChange is too soon
public ngOnChanges(changes: {[key: string]: SimpleChange}): void
{
if (this.wasViewInitialized) {
return;
}
this.refreshContent();
}
public ngOnDestroy(){
if (this.componentRef) {
this.componentRef.destroy();
this.componentRef = null;
}
}
Run Code Online (Sandbox Code Playgroud)
这就是它.不要忘记销毁任何动态构建的东西(ngOnDestroy).此外,请确保缓存动态types,modules如果唯一的区别是他们的模板.
在这里检查一切
要查看此帖子的先前版本(例如RC5相关),请查看历史记录
Ren*_*ger 54
编辑(2017年8月26日):下面的解决方案适用于Angular2和4.我已将其更新为包含模板变量并单击处理程序并使用Angular 4.3进行测试.
对于Angular4,Ophir的答案中描述的ngComponentOutlet 是一个更好的解决方案.但是现在它还不支持输入和输出.如果[this PR](https://github.com/angular/angular/pull/15362)被接受,则可以通过create事件返回的组件实例
.ng-dynamic-component可能是最好和最简单的完全解决方案,但我还没有测试过.
@Long Field的答案就是现场!这是另一个(同步)示例:
import {Compiler, Component, NgModule, OnInit, ViewChild,
ViewContainerRef} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
@Component({
selector: 'my-app',
template: `<h1>Dynamic template:</h1>
<div #container></div>`
})
export class App implements OnInit {
@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private compiler: Compiler) {}
ngOnInit() {
this.addComponent(
`<h4 (click)="increaseCounter()">
Click to increase: {{counter}}
`enter code here` </h4>`,
{
counter: 1,
increaseCounter: function () {
this.counter++;
}
}
);
}
private addComponent(template: string, properties?: any = {}) {
@Component({template})
class TemplateComponent {}
@NgModule({declarations: [TemplateComponent]})
class TemplateModule {}
const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
const factory = mod.componentFactories.find((comp) =>
comp.componentType === TemplateComponent
);
const component = this.container.createComponent(factory);
Object.assign(component.instance, properties);
// If properties are changed at a later stage, the change detection
// may need to be triggered manually:
// component.changeDetectorRef.detectChanges();
}
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
Run Code Online (Sandbox Code Playgroud)
住在http://plnkr.co/edit/fdP9Oc.
小智 49
我一定是迟到了,这里没有一个解决方案似乎对我有帮助 - 太乱了,觉得太多了解决方法.
我最终做的是使用Angular 4.0.0-beta.6' ngComponentOutlet.
这给了我所有写在动态组件文件中的最简单,最简单的解决方案.
import {
Component, OnInit, Input, NgModule, NgModuleFactory, Compiler
} from '@angular/core';
@Component({
selector: 'my-component',
template: `<ng-container *ngComponentOutlet="dynamicComponent;
ngModuleFactory: dynamicModule;"></ng-container>`,
styleUrls: ['my.component.css']
})
export class MyComponent implements OnInit {
dynamicComponent;
dynamicModule: NgModuleFactory<any>;
@Input()
text: string;
constructor(private compiler: Compiler) {
}
ngOnInit() {
this.dynamicComponent = this.createNewComponent(this.text);
this.dynamicModule = this.compiler.compileModuleSync(this.createComponentModule(this.dynamicComponent));
}
protected createComponentModule (componentType: any) {
@NgModule({
imports: [],
declarations: [
componentType
],
entryComponents: [componentType]
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}
protected createNewComponent (text:string) {
let template = `dynamically created template with text: ${text}`;
@Component({
selector: 'dynamic-component',
template: template
})
class DynamicComponent implements OnInit{
text: any;
ngOnInit() {
this.text = text;
}
}
return DynamicComponent;
}
}
Run Code Online (Sandbox Code Playgroud)
my-component - 动态组件呈现的组件DynamicComponent - 要动态构建的组件,它在my-component中呈现不要忘记将所有角度库升级到^ Angular 4.0.0
希望这有帮助,祝你好运!
UPDATE
也适用于角5.
Ste*_*aul 16
我决定将我学到的所有东西压缩成一个文件.与RC5之前相比,这里有很多东西需要考虑.请注意,此源文件包含AppModule和AppComponent.
import {
Component, Input, ReflectiveInjector, ViewContainerRef, Compiler, NgModule, ModuleWithComponentFactories,
OnInit, ViewChild
} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
@Component({
selector: 'app-dynamic',
template: '<h4>Dynamic Components</h4><br>'
})
export class DynamicComponentRenderer implements OnInit {
factory: ModuleWithComponentFactories<DynamicModule>;
constructor(private vcRef: ViewContainerRef, private compiler: Compiler) { }
ngOnInit() {
if (!this.factory) {
const dynamicComponents = {
sayName1: {comp: SayNameComponent, inputs: {name: 'Andrew Wiles'}},
sayAge1: {comp: SayAgeComponent, inputs: {age: 30}},
sayName2: {comp: SayNameComponent, inputs: {name: 'Richard Taylor'}},
sayAge2: {comp: SayAgeComponent, inputs: {age: 25}}};
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then((moduleWithComponentFactories: ModuleWithComponentFactories<DynamicModule>) => {
this.factory = moduleWithComponentFactories;
Object.keys(dynamicComponents).forEach(k => {
this.add(dynamicComponents[k]);
})
});
}
}
addNewName(value: string) {
this.add({comp: SayNameComponent, inputs: {name: value}})
}
addNewAge(value: number) {
this.add({comp: SayAgeComponent, inputs: {age: value}})
}
add(comp: any) {
const compFactory = this.factory.componentFactories.find(x => x.componentType === comp.comp);
// If we don't want to hold a reference to the component type, we can also say: const compFactory = this.factory.componentFactories.find(x => x.selector === 'my-component-selector');
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
const cmpRef = this.vcRef.createComponent(compFactory, this.vcRef.length, injector, []);
Object.keys(comp.inputs).forEach(i => cmpRef.instance[i] = comp.inputs[i]);
}
}
@Component({
selector: 'app-age',
template: '<div>My age is {{age}}!</div>'
})
class SayAgeComponent {
@Input() public age: number;
};
@Component({
selector: 'app-name',
template: '<div>My name is {{name}}!</div>'
})
class SayNameComponent {
@Input() public name: string;
};
@NgModule({
imports: [BrowserModule],
declarations: [SayAgeComponent, SayNameComponent]
})
class DynamicModule {}
@Component({
selector: 'app-root',
template: `
<h3>{{message}}</h3>
<app-dynamic #ad></app-dynamic>
<br>
<input #name type="text" placeholder="name">
<button (click)="ad.addNewName(name.value)">Add Name</button>
<br>
<input #age type="number" placeholder="age">
<button (click)="ad.addNewAge(age.value)">Add Age</button>
`,
})
export class AppComponent {
message = 'this is app component';
@ViewChild(DynamicComponentRenderer) dcr;
}
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, DynamicComponentRenderer],
bootstrap: [AppComponent]
})
export class AppModule {}`
Run Code Online (Sandbox Code Playgroud)
Ste*_*aul 13
2019年6月答案
好消息!看来@ angular / cdk软件包现在已经对门户提供了一流的支持!
在撰写本文时,我没有发现上述官方文档特别有用(特别是在向动态组件发送数据和从动态组件接收事件方面)。总之,您将需要:
步骤1)更新您的 AppModule
PortalModule从cdk包中导入并在其中注册动态组件entryComponents
@NgModule({
declarations: [ ..., AppComponent, MyDynamicComponent, ... ]
imports: [ ..., PortalModule, ... ],
entryComponents: [ ..., MyDynamicComponent, ... ]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)
步骤2.选项A:如果不需要将数据传递到动态组件中或从动态组件中接收事件,则:
@Component({
selector: 'my-app',
template: `
<button (click)="onClickAddChild()">Click to add child component</button>
<ng-template [cdkPortalOutlet]="myPortal"></ng-template>
`
})
export class AppComponent {
myPortal: ComponentPortal<any>;
onClickAddChild() {
this.myPortal = new ComponentPortal(MyDynamicComponent);
}
}
@Component({
selector: 'app-child',
template: `<p>I am a child.</p>`
})
export class MyDynamicComponent{
}
Run Code Online (Sandbox Code Playgroud)
步骤2.选项B:如果您确实需要将数据传递到动态组件中并从动态组件中接收事件,则:
// A bit of boilerplate here. Recommend putting this function in a utils
// file in order to keep your component code a little cleaner.
function createDomPortalHost(elRef: ElementRef, injector: Injector) {
return new DomPortalHost(
elRef.nativeElement,
injector.get(ComponentFactoryResolver),
injector.get(ApplicationRef),
injector
);
}
@Component({
selector: 'my-app',
template: `
<button (click)="onClickAddChild()">Click to add random child component</button>
<div #portalHost></div>
`
})
export class AppComponent {
portalHost: DomPortalHost;
@ViewChild('portalHost') elRef: ElementRef;
constructor(readonly injector: Injector) {
}
ngOnInit() {
this.portalHost = createDomPortalHost(this.elRef, this.injector);
}
onClickAddChild() {
const myPortal = new ComponentPortal(MyDynamicComponent);
const componentRef = this.portalHost.attach(myPortal);
setTimeout(() => componentRef.instance.myInput
= '> This is data passed from AppComponent <', 1000);
// ... if we had an output called 'myOutput' in a child component,
// this is how we would receive events...
// this.componentRef.instance.myOutput.subscribe(() => ...);
}
}
@Component({
selector: 'app-child',
template: `<p>I am a child. <strong>{{myInput}}</strong></p>`
})
export class MyDynamicComponent {
@Input() myInput = '';
}
Run Code Online (Sandbox Code Playgroud)
我有一个简单的例子来展示如何做角度2 rc6动态组件.
比如说,你有一个动态的html模板= template1并想要动态加载,首先包装到组件中
@Component({template: template1})
class DynamicComponent {}
Run Code Online (Sandbox Code Playgroud)
这里template1为html,可能包含ng2组件
从rc6开始,必须有@NgModule包装这个组件.@NgModule,就像anglarJS 1中的模块一样,它解耦了ng2应用程序的不同部分,因此:
@Component({
template: template1,
})
class DynamicComponent {
}
@NgModule({
imports: [BrowserModule,RouterModule],
declarations: [DynamicComponent]
})
class DynamicModule { }
Run Code Online (Sandbox Code Playgroud)
(这里导入RouterModule,就像我的例子中一样,我的html中有一些路由组件,你可以在后面看到)
现在您可以将DynamicModule编译为:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then(
factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
我们需要在app.moudule.ts上面加载它,请看我的app.moudle.ts.有关更多详细信息,请访问:https: //github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts和app.moudle.ts
并看到演示:http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p = preview
到 2021 年,Angular 仍然无法使用动态 HTML 创建组件(动态加载 html 模板),只是为了节省您的时间。
即使有很多已投票通过的解决方案和已接受的解决方案,但至少目前它们都不适用于生产/AOT 中的最新版本。
基本上是因为 Angular 不允许您使用以下方式定义组件: template: {variable}
正如 Angular 团队所说,他们不会支持这种方法!请找到此参考https://github.com/angular/angular/issues/15275
跟进 Radmin 的出色回答,使用 angular-cli 1.0.0-beta.22 及更高版本的每个人都需要进行一些调整。
COMPILER_PROVIDERS无法再导入(有关详细信息,请参阅angular-cli GitHub)。
因此,解决方法是根本不使用COMPILER_PROVIDERSandJitCompiler在该providers部分中,而是JitCompilerFactory在类型构建器类中使用from '@angular/compiler' 而不是这样:
private compiler: Compiler = new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
Run Code Online (Sandbox Code Playgroud)
如您所见,它不可注入,因此与 DI 没有依赖关系。此解决方案也适用于不使用 angular-cli 的项目。
只需使用ng-dynamic中的dynamicComponent指令,就可以在Angular 2 Final版本中解决这个问题.
用法:
<div *dynamicComponent="template; context: {text: text};"></div>
Run Code Online (Sandbox Code Playgroud)
其中template是您的动态模板,上下文可以设置为您希望模板绑定到的任何动态数据模型.
在角度7.x中,我为此使用了角度元素。
在@ angular / elements -s中安装@ angular-elements npm
创建附件服务。
import { Injectable, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { IStringAnyMap } from 'src/app/core/models';
import { AppUserIconComponent } from 'src/app/shared';
const COMPONENTS = {
'user-icon': AppUserIconComponent
};
@Injectable({
providedIn: 'root'
})
export class DynamicComponentsService {
constructor(private injector: Injector) {
}
public register(): void {
Object.entries(COMPONENTS).forEach(([key, component]: [string, any]) => {
const CustomElement = createCustomElement(component, { injector: this.injector });
customElements.define(key, CustomElement);
});
}
public create(tagName: string, data: IStringAnyMap = {}): HTMLElement {
const customEl = document.createElement(tagName);
Object.entries(data).forEach(([key, value]: [string, any]) => {
customEl[key] = value;
});
return customEl;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,自定义元素标签必须与角度分量选择器不同。在AppUserIconComponent中:
...
selector: app-user-icon
...
Run Code Online (Sandbox Code Playgroud)
在这种情况下,自定义标签名称使用的是“ user-icon”。
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent {
constructor(
dynamicComponents: DynamicComponentsService,
) {
dynamicComponents.register();
}
}
Run Code Online (Sandbox Code Playgroud)
dynamicComponents.create('user-icon', {user:{...}});
Run Code Online (Sandbox Code Playgroud)
或像这样:
const html = `<div class="wrapper"><user-icon class="user-icon" user='${JSON.stringify(rec.user)}'></user-icon></div>`;
this.content = this.domSanitizer.bypassSecurityTrustHtml(html);
Run Code Online (Sandbox Code Playgroud)
(在模板中):
<div class="comment-item d-flex" [innerHTML]="content"></div>
Run Code Online (Sandbox Code Playgroud)
请注意,在第二种情况下,您必须传递带有JSON.stringify的对象,然后再对其进行解析。我找不到更好的解决方案。
| 归档时间: |
|
| 查看次数: |
128632 次 |
| 最近记录: |