템플릿을 동적으로 만들고 싶습니다. 이것은을 구축하는 데 사용되어야 ComponentType
런타임과 장소에서 (심지어 대체) 어딘가에 내부 호스팅 구성 요소의.
RC4까지는을 사용 ComponentResolver
했지만 RC5에서는 다음 메시지가 표시됩니다.
ComponentResolver is deprecated for dynamic compilation.
Use ComponentFactoryResolver together with @NgModule/@Component.entryComponents or ANALYZE_FOR_ENTRY_COMPONENTS provider instead.
For runtime compile only, you can also use Compiler.compileComponentSync/Async.
이 문서를 찾았습니다 ( Angular 2 Synchronous Dynamic Component Creation )
그리고 어느 쪽이든 사용할 수 있다는 것을 이해하십시오
- 동적의 종류
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>
...
다른 경우 이것은 하나 ( string-editor
치환된다 text-editor
)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
그리고 등등 ( editors
속성 유형에 따라 다른 번호 / 날짜 / 참조 , 일부 사용자의 일부 속성을 건너 뛰었습니다 …) . 즉, 이것은 실제 구성이 훨씬 더 다르고 복잡한 템플릿을 생성 할 수있는 예입니다.
템플릿이 변경되어 ComponentFactoryResolver
기존 템플릿을 사용 하고 전달할 수 없습니다 Compiler
. 의 솔루션이 필요합니다 .
답변
편집 -2.3.0 관련 (2016-12-07)
참고 : 이전 버전에 대한 솔루션을 얻으려면이 게시물의 기록을 확인하십시오.
비슷한 주제가 여기에서 논의됩니다 . Angular 2의 $ compile과 동일 합니다. 우리는 사용해야 JitCompiler
하고 NgModule
. NgModule
Angular2 에 대한 자세한 내용 은 여기를 참조하십시오.
간단히 말해서
가 작동 plunker / 예 (동적 템플릿 동적 성분계 동적 모듈, JitCompiler
… 액션)
교장은이다 :
1) 생성 템플릿
2) 찾을 수 ComponentFactory
캐시에 – 로 이동 7)
3) – 작성 Component
4) – 생성 Module
5) – 컴파일 Module
6) – 리턴 (나중에 사용할 수 있도록 캐시) ComponentFactory
7) 사용 대상 및 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;
//...
});
이것은 간단합니다. 자세한 내용은 아래를 참조하십시오.
.
TL & DR
일부 스 니펫에 추가 설명이 필요한 경우 플런저를 관찰하고 세부 정보를 다시 읽으십시오.
.
자세한 설명-Angular2 RC6 ++ 및 런타임 구성 요소
의 설명 아래 이 경우 , 우리는 것입니다
- 모듈 만들기
PartsModule:NgModule
(작은 조각 보유자) DynamicModule:NgModule
동적 구성 요소를 포함하는 다른 모듈을 작성하십시오 ( 동적 참조PartsModule
).- 동적 템플릿 생성 (간단한 접근)
- 새
Component
유형 만들기 (템플릿이 변경된 경우에만) - 새로 만듭니다
RuntimeModule:NgModule
. 이 모듈에는 이전에 생성 된Component
유형 이 포함됩니다 - 전화
JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
하다ComponentFactory
DynamicComponent
View Target 자리 표시 자의 작업 인스턴스를 생성 하고ComponentFactory
- 지정
@Inputs
에 새로운 인스턴스 (에서 스위치INPUT
로TEXTAREA
편집) , 소비@Outputs
NgModule
우리는 NgModule
s 가 필요합니다 .
매우 간단한 예제를 보여주고 싶지만이 경우에는 세 개의 모듈이 필요합니다 (사실 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 { }
어디는
DYNAMIC_DIRECTIVES
확장 가능하고 동적 구성 요소 템플릿 / 유형에 사용되는 모든 작은 부품을 유지하기위한 것입니다. 확인 응용 프로그램 / 부품 / parts.module.ts을
두 번째는 Dynamic stuff 처리를위한 모듈입니다. 호스팅 구성 요소와 일부 공급자가 포함됩니다. 이를 위해 표준 방식으로 게시합니다.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
],
};
}
}
의 사용 확인
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
],
NgModule 에 관해 훨씬 더 많은 것을 읽고 (읽으십시오) :
템플릿 빌더
이 예에서는 이러한 유형의 엔티티 에 대한 세부 사항을 처리합니다.
entity = {
code: "ABC123",
description: "A description of this Entity"
};
을 만들기 template
위해이 플 런커 에서이 단순 / 순진 빌더를 사용합니다.
실제 템플릿 빌더 인 실제 솔루션은 애플리케이션이 많은 작업을 수행 할 수있는 곳입니다.
// 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>";
}
}
여기서 트릭은 알려진 속성 집합을 사용하는 템플릿을 작성하는 것 entity
입니다. 이러한 속성 (-ies)은 다음에 만들 동적 구성 요소의 일부 여야합니다.
좀 더 쉽게 만들기 위해 인터페이스를 사용하여 템플릿 빌더가 사용할 수있는 속성을 정의 할 수 있습니다. 이는 동적 컴포넌트 유형으로 구현됩니다.
export interface IHaveDynamicData {
public entity: any;
...
}
ComponentFactory
빌더
여기서 매우 중요한 것은 명심해야합니다.
우리와 함께 빌드 한 컴포넌트 유형
DynamicTypeBuilder
은 템플릿에 따라 다를 수 있습니다 (위에서 생성) . 구성 요소의 특성 (입력, 출력 또는 일부 보호)은 여전히 동일합니다. 다른 속성이 필요한 경우 템플릿과 유형 작성기의 다른 조합을 정의해야합니다
따라서 우리는 솔루션의 핵심을 감동시키고 있습니다. 빌더는 1) 작성 ComponentType
2) 작성 NgModule
3) 컴파일 ComponentFactory
4) 나중에 재사용 할 수 있도록 캐시 합니다.
우리가 받아야 할 의존성 :
// plunker - app/dynamic/type.builder.ts
import { JitCompiler } from '@angular/compiler';
@Injectable()
export class DynamicTypeBuilder {
// wee need Dynamic component builder
constructor(
protected compiler: JitCompiler
) {}
다음은 스 니펫을 얻는 방법입니다 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);
});
});
}
우리가 만들고 위의 캐시 모두
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;
}
중대한:
컴포넌트 동적 유형은 템플릿마다 다릅니다. 그래서 우리는 그 사실 을 사용 하여 그것들 을 캐시 합니다. 이것은 매우 중요합니다. Angular2는 이것들을 유형별로 캐시 할 것 입니다. 동일한 템플릿 문자열에 대해 새 유형을 다시 만들면 메모리 누수가 발생하기 시작합니다.
ComponentFactory
호스팅 구성 요소에서 사용
마지막 부분은 예를 들어 동적 구성 요소의 대상을 호스팅하는 구성 요소 <div #dynamicContentPlaceHolder></div>
입니다. 우리는 그것에 대한 참조를 얻고 ComponentFactory
구성 요소를 만드는 데 사용 합니다. 그것은 요컨대, 여기에 그 구성 요소의 모든 조각이 있습니다 (필요한 경우 여기에서 plunker를여십시오 )
먼저 import 문을 요약 해 봅시다 :
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
) {}
...
우리는 단지 템플릿과 컴포넌트 빌더를받습니다. 다음은 예제에 필요한 속성입니다 (자세한 설명 참조).
// 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"
};
이 간단한 시나리오에서 호스팅 구성 요소에는이 없습니다 @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;
//...
});
}
작은 확장
또한 컴파일 된 템플릿에 대한 참조를 유지해야 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;
}
}
끝난
그것은 거의 다입니다. 동적으로 구축 된 것을 파괴하는 것을 잊지 마십시오 (ngOnDestroy) . 또한, 반드시 캐시의 동적 types
및 modules
유일한 차이점은 템플릿의 경우.
이 게시물의 이전 버전 (예 : RC5 관련) 을 보려면 기록을 확인하십시오.
답변
편집 (26/08/2017) : 아래 솔루션은 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 {}
답변
나는 파티에 늦게 도착했을 것입니다. 여기에서 해결책 중 어느 것도 나에게 도움이되지 않았습니다. 너무 지저분하고 너무 많은 해결책처럼 느껴졌습니다.
내가하고 결국은 사용하고 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;
}
}
- 간단한 설명 :
my-component
-동적 컴퍼넌트가 렌더링되는 컴퍼넌트DynamicComponent
-동적으로 작성 될 컴포넌트이며 my-component 내부에서 렌더링 중입니다.
모든 각도 라이브러리를 ^ Angular 4.0.0으로 업그레이드하는 것을 잊지 마십시오
이것이 도움이되기를 바랍니다. 행운을 빕니다!
최신 정보
각도 5에서도 작동합니다.
답변
2019 년 6 월 답변
좋은 소식! @ angular / cdk 패키지는 이제 포털에 대한 일류 지원을 제공 하는 것 같습니다 !
글을 쓰는 시점에서 위의 공식 문서는 특히 도움이되지 않았습니다 (특히 동적 구성 요소로 데이터를 보내고 이벤트를받는 것과 관련하여). 요약하면 다음이 필요합니다.
1 단계) 업데이트 AppModule
패키지 PortalModule
에서 가져 와서 @angular/cdk/portal
동적 컴포넌트를 내부에 등록하십시오entryComponents
@NgModule({
declarations: [ ..., AppComponent, MyDynamicComponent, ... ]
imports: [ ..., PortalModule, ... ],
entryComponents: [ ..., MyDynamicComponent, ... ]
})
export class AppModule { }
단계 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{
}
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 = '';
}
답변
나는 배운 모든 것을 하나의 파일로 압축하기로 결정했다 . 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 {}`
답변
각도 2 rc6 동적 구성 요소를 수행하는 방법을 보여주는 간단한 예가 있습니다.
예를 들어 동적 HTML 템플릿 = template1이 있고 동적으로로드하고 싶다면 먼저 구성 요소를 래핑하십시오.
@Component({template: template1})
class DynamicComponent {}
여기에 template1을 html로, ng2 구성 요소가 포함될 수 있습니다
rc6부터는 @NgModule이이 컴포넌트를 감싸도록해야합니다. @NgModule은 anglarJS 1의 모듈과 마찬가지로 ng2 응용 프로그램의 다른 부분을 분리합니다.
@Component({
template: template1,
})
class DynamicComponent {
}
@NgModule({
imports: [BrowserModule,RouterModule],
declarations: [DynamicComponent]
})
class DynamicModule { }
(이 예제에서와 같이 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를 확인하십시오.
답변
각도 7.x에서는 각도 요소를 사용했습니다.
-
@ angular-elements npm i @ angular / elements -s 설치
-
액세서리 서비스를 만듭니다.
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;
}
}
사용자 요소 태그는 각도 구성 요소 선택기와 달라야합니다. AppUserIconComponent에서 :
...
selector: app-user-icon
...
이 경우 사용자 정의 태그 이름은 “user-icon”을 사용했습니다.
- 그런 다음 AppComponent에서 register를 호출해야합니다.
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent {
constructor(
dynamicComponents: DynamicComponentsService,
) {
dynamicComponents.register();
}
}
- 이제 코드의 어느 곳에서나 다음과 같이 사용할 수 있습니다.
dynamicComponents.create('user-icon', {user:{...}});
또는 이와 같이 :
const html = `<div class="wrapper"><user-icon class="user-icon" user='${JSON.stringify(rec.user)}'></user-icon></div>`;
this.content = this.domSanitizer.bypassSecurityTrustHtml(html);
(템플릿에서) :
<div class="comment-item d-flex" [innerHTML]="content"></div>
두 번째 경우 JSON.stringify를 사용하여 객체를 전달한 후 다시 구문 분석해야합니다. 더 나은 해결책을 찾을 수 없습니다.