다이내믹 템플릿을 사용하여 Angular 2.0을 사용하여 다이내믹 컴포넌트를 컴파일하려면 어떻게 해야 합니까?
템플릿을 동적으로 만들고 싶다.은, 「 」의 할 필요가 있습니다.ComponentType실행 시 호스트 컴포넌트 내부 어딘가에 배치(교체까지 가능)합니다.
까지는 RC4를 사용하고 .ComponentResolver같은 됩니다.
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).
다음 중 하나를 사용할 수 있습니다.
- 한 타입의 'Dynamic(다이나믹한'
ngIfComponentFactoryResolver@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으로 대체)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
(속성 유형별로 다른 수/날짜/참조, 일부 사용자에 대한 일부 속성 건너뛰기 등). 즉, 실제 구성은 훨씬 더 다양하고 복잡한 템플릿을 생성할 수 있습니다.
되고 있기 할 수 .ComponentFactoryResolver…는 이 문제를 이 필요하다.Compiler.
편집 - 2.3.0(2016-12-07) 관련
메모: 이전 버전의 솔루션을 얻으려면 이 게시물의 이력을 확인하십시오.
유사한 주제는 Angular 2의 $compile과 동등하다.사용할 필요가 있다JitCompiler ★★★★★★★★★★★★★★★★★」NgModuleAngular2에 대한 자세한 내용은 여기를 참조하십시오.
간단히 말하면
작동하는 플런커/예가 있습니다(동적 템플릿, 동적 구성 요소 유형,JitCompiler 동적 모듈 등).
을 사용하다
1) 템플릿 작성
2) 검색ComponentFactory캐시 내 - 7로 이동)
) - 3) - 작성Component
) - 4) - 작성Module
- 컴파일 5) - 컴파일 5 - 컴파일 5Module
- return (및사용하기 ) 6 - return (캐시)ComponentFactory
7) 타겟과ComponentFactorydynamic dynamic 를 Component
여기 코드 스니펫이 있습니다(자세한 내용은 이쪽). 커스텀 빌더가 빌드/캐시된 상태로 반환됩니다.ComponentFactory및 타겟 플레이스홀더에 의한 타겟 플레이스홀더에 의한
// 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동적 컴포넌트(및 동적으로 참조)가 포함됩니다. - 동적 템플릿 작성(간단한 접근법)
- 것을
Componenttype(템플릿이 변경된 경우에만 해당) - 것을
RuntimeModule:NgModule. 이에 작성한 '이 모듈'이Component JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)ComponentFactory- 의 인스턴스를 만듭니다.
DynamicComponentTarget 및 - View Target 작업ComponentFactory @Inputs새로운 인스턴스(에서 편집으로 전환)를 사용하다@Outputs
Ng 모듈
는 ★★★★★★★★★★★★★★★★가 필요합니다.NgModules.
매우 간단한 예를 제시하겠습니다만, 이 경우는 3개의 모듈이 필요합니다(사실상 4개입니다만, AppModule은 세지 않습니다).단순한 스니펫이 아니라 매우 견고한 동적 컴포넌트 생성기의 기반으로 삼으십시오.
모든 소형 컴포넌트에 1개의 모듈이 있습니다(예:string-editor,text-editor )date-editor,number-editor...)
@NgModule({
imports: [
CommonModule,
FormsModule
],
declarations: [
DYNAMIC_DIRECTIVES
],
exports: [
DYNAMIC_DIRECTIVES,
CommonModule,
FormsModule
]
})
export class PartsModule { }
는 확장 가능하며 동적 구성 요소 템플릿/유형에 사용되는 모든 작은 부품을 고정하기 위한 것입니다.app/parts/parts.module.ts를 확인합니다.
두 번째 모듈은 동적 처리 모듈입니다.인 방법으로 하겠습니다.- 그 、 준 、 준 、 준 、 준 、 준 、 준 、 준 - way 。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
그것은 나중에 입니다.DynamicTypeBuilderdisplays 。
네 번째 모듈인 어플리케이션모듈은 컴파일러 프로바이더를 선언합니다.
...
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에서는 이 심플하고 간단한 빌더를 사용합니다.
진정한 솔루션인 템플릿 빌더는 애플리케이션이 많은 작업을 수행할 수 있는 장소입니다.
// 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>";
}
}
여기서의 트릭은, 예를 들면, 몇개의 기존의 속성 세트를 사용하는 템플릿을 작성하는 것입니다.이러한 속성(-ees)은 다음에 작성하는 동적 컴포넌트의 일부여야 합니다.
좀 더 쉽게 하기 위해 인터페이스를 사용하여 속성을 정의할 수 있습니다.속성은 템플릿빌더에서 사용할 수 있습니다.이것은 델의 동적 컴포넌트 타입으로 구현됩니다.
export interface IHaveDynamicData {
public entity: any;
...
}
A ComponentFactory
여기서 매우 중요한 것은 다음과 같습니다.
의 컴포넌트 델의 컴포넌트 타입으로 , 델의 컴포넌트 타입으로 .
DynamicTypeBuilder는 다를 수 있지만 템플릿(상기 작성)에 의해서만 다릅니다.컴포넌트 속성(입력, 출력 또는 일부 보호)은 여전히 동일합니다.다른 속성이 필요한 경우 템플릿과 유형 작성기의 다른 조합을 정의해야 합니다.
그래서 우리는 솔루션의 핵심을 다루고 있습니다.가 1)ComponentType 2) 작성NgModule 컴파일 3) 컴파일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
) {}
그리고 여기 어떻게 해야 할지가 있습니다.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컴포넌트를 만듭니다.간단히 말하면, 이 컴포넌트의 모든 부품은 다음과 같습니다(필요한 경우 open 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타겟 플레이스 홀더는 해당 팩토리에서 를 인스턴스화하도록 요구됩니다.
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;
//...
});
}
소확장
또한 컴파일된 템플릿에 대한 참조를 유지해야 합니다.우리가 바꿀 때마다 제대로 할 수 있도록 말이야
// 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 관련)을 보려면 이력을 확인하십시오.
편집 (2017년 10월 26일) :아래 솔루션은 Angular2와 4에서 잘 작동합니다.템플릿 변수를 포함하도록 업데이트하고 핸들러를 클릭하여 Angular 4.3으로 테스트했습니다.
Angular4의 경우, Ofir의 답변에 설명된 ngComponentOutlet이 훨씬 더 나은 솔루션입니다.다만, 현시점에서는, 입력과 출력을 서포트하고 있지 않습니다.[ this PR ] ( https://github.com/angular/angular/pull/15362 )가 받아들여지면 create 이벤트에서 반환된 컴포넌트인스턴스를 통해 가능합니다.
ng-dynamic-component가 가장 좋고 간단한 솔루션일 수 있지만 아직 테스트하지 않았습니다.
@롱필드의 대답은 딱 들어맞았다!다음은 다른 (동기식) 예입니다.
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 {}
http://plnkr.co/edit/fdP9Oc에 접속해 주세요.
제가 파티에 늦게 도착했었나 봐요.여기 있는 어떤 해결책도 도움이 되지 않는 것 같았습니다.너무 지저분하고 회피책처럼 느껴졌어요.
된 은 '아예'를 사용한 입니다.Angular 4.0.0-beta.6의 ng Component Outlet 입니다.
이를 통해 동적 구성 요소의 파일에 모두 기록된 가장 짧고 간단한 솔루션을 얻을 수 있었습니다.
- 다음은 텍스트를 수신하여 템플릿에 넣는 간단한 예입니다.단, 필요에 따라 변경할 수 있습니다.
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는 my-component입니다.
모든 각도 라이브러리를 ^Angular 4.0.0으로 업그레이드하는 것을 잊지 마십시오.
이게 도움이 됐으면 좋겠네요, 행운을 빌어요!
갱신하다
각도 5에도 효과가 있습니다.
2019년 6월 답변
좋은 소식!@angular/cdk 패키지가 포털을 1등급으로 지원하게 되었습니다!
작성 시점에서는 위의 공식 문서(특히 동적 컴포넌트와의 데이터 송수신 관련)가 특별히 도움이 되지 않았습니다.요약하면 다음과 같습니다.
1) 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 {}`
angular 2 rc6 dynamic component를 수행하는 방법을 보여 주는 간단한 예가 있습니다.
예를 들어, 동적 html 템플릿 = template1이 있으며 동적 로드를 원하는 경우 먼저 구성 요소로 래핑합니다.
@Component({template: template1})
class DynamicComponent {}
여기서 template1은 html로서 ng2 컴포넌트를 포함할 수 있습니다.
rc6부터 이 컴포넌트를 @NgModule로 랩해야 합니다.@NgModule, 앵글라의 모듈과 같은JS 1, ng2 어플리케이션의 다른 부분을 분리하기 위해 다음과 같이 합니다.
@Component({
template: template1,
})
class DynamicComponent {
}
@NgModule({
imports: [BrowserModule,RouterModule],
declarations: [DynamicComponent]
})
class DynamicModule { }
(여기서 RouterModule을 Import합니다.이 예에서는 html에 루트컴포넌트가 포함되어 있습니다.나중에 보실 수 있습니다).
Dynamic 을 "Dynamic Module"로할 수 .this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
그리고 우리는 그것을 로드하기 위해 app.mouschedule.ts에 위 내용을 넣어야 합니다.제 app.moudle.ts를 봐주세요.자세한 내용은 https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts 및 app.moudlets를 참조하십시오.
데모를 참조해 주세요.
angular 7.x에서는 angular elements를 사용했습니다.
@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
...
이 경우 커스텀태그명을사용했습니다.
- 그런 다음 AppComponent에서 등록을 호출해야 합니다.
@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를 사용하여 객체를 전달하고 그 후 다시 해석해야 합니다.더 나은 해결책을 찾을 수가 없어요.
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_PROVIDERS ★★★★★★★★★★★★★★★★★」JitCompiler providers전혀 하지 않지만, 섹섹전만만만만만만만만만만만만만만만을 사용합니다.JitCompilerFactory@filder/filder'는 '@filder/filder'를 말합니다.
private compiler: Compiler = new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
보시는 바와 같이 DI는 주입할 수 없기 때문에 DI에 의존하지 않습니다.이 솔루션은 angular-cli를 사용하지 않는 프로젝트에서도 사용할 수 있습니다.
Angular 2 Final 버전에서는 ng-dynamic의 dynamic Component 디렉티브를 사용하여 이 문제를 해결했습니다.
사용방법:
<div *dynamicComponent="template; context: {text: text};"></div>
여기서 template는 다이내믹템플릿으로 컨텍스트는 템플릿을 바인드하는 임의의 다이내믹데이터모델로 설정할 수 있습니다.
Radim의 매우 훌륭한 게시물 위에 몇 가지 세부사항을 추가하고 싶다.
저는 이 솔루션을 사용하여 잠시 작업을 진행했지만, 곧 몇 가지 한계에 부딪혔습니다.저는 그것들을 개략적으로 설명하고 그 해결책도 제시하겠습니다.
- 우선, 다이나믹 디테일에 dynamic-detail을 렌더링 할 수 없었습니다(기본적으로 다이나믹 UI를 서로 중첩).
- 다음 문제는 솔루션에서 사용할 수 있는 부품 중 하나에 다이내믹 디테일을 렌더링하고 싶다는 것이었습니다.그것은 초기 해결책으로도 가능하지 않았다.
- 마지막으로 문자열 편집기와 같은 동적 부분에서는 템플릿 URL을 사용할 수 없었습니다.
이 투고에 근거해, 다음의 제한 사항을 어떻게 달성할 것인가에 대해 또 다른 질문을 했습니다.
recursive 다이내믹템플릿 컴파일(angular2
이러한 제한에 대한 해답의 개요를 설명하겠습니다.저와 같은 문제에 직면하게 되면 솔루션의 유연성이 높아집니다.그것도 초기 플런커를 업데이트해 주시면 감사하겠습니다.
서로 네스트하는 다이내믹 디테일을 이노블로 만들려면 유형의 Import 문에 Dynamic Module.forRoot()를 추가해야 합니다.빌더.ts
protected createComponentModule (componentType: any) {
@NgModule({
imports: [
PartsModule,
DynamicModule.forRoot() //this line here
],
declarations: [
componentType
],
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}
할 수 .<dynamic-detail>문자열 변환 또는 텍스트 변환 중 하나의 부품 안에 있습니다.
하게 하려면 , 「」를 변경할 .parts.module.ts ★★★★★★★★★★★★★★★★★」dynamic.module.ts
parts.module.ts 해서 '어울리지 않다'를 넣어야 .DynamicDetail DYNAMIC_DIRECTIVES
export const DYNAMIC_DIRECTIVES = [
forwardRef(() => StringEditor),
forwardRef(() => TextEditor),
DynamicDetail
];
,에서는dynamic.module.ts은 이제 partsdynamic Detail의 .
@NgModule({
imports: [ PartsModule ],
exports: [ PartsModule],
})
동작하고 있는 수정 플러그는, http://plnkr.co/edit/UYnQHF?p=preview 를 참조해 주세요(이 문제는 해결하지 않았습니다.메신저일 뿐입니다:-D).
마지막으로 동적 컴포넌트에 작성된 부품에서는 템플릿을 사용할 수 없었습니다.해결책(또는 회피책).각진 버그인지 프레임워크의 잘못된 사용인지는 잘 모르겠습니다.) 컴파일러를 삽입하는 대신 컨스트럭터에 컴파일러를 만드는 것이었습니다.
private _compiler;
constructor(protected compiler: RuntimeCompiler) {
const compilerFactory : CompilerFactory =
platformBrowserDynamic().injector.get(CompilerFactory);
this._compiler = compilerFactory.createCompiler([]);
}
, 그럼 이번에는 '어 주세요'를 하세요._compilertemplateUrls 。
return new Promise((resolve) => {
this._compiler
.compileModuleAndAllComponentsAsync(module)
.then((moduleWithFactories) =>
{
let _ = window["_"];
factory = _.find(moduleWithFactories.componentFactories, { componentType: type });
this._cacheOfFactories[template] = factory;
resolve(factory);
});
});
이게 다른 사람에게 도움이 되길 바라!
Morten씨께 안부 전해주세요.
다음은 서버에서 생성된 동적 양식 컨트롤의 예입니다.
https://stackblitz.com/edit/angular-t3mmg6
이 예는 동적 양식 컨트롤이 추가 구성 요소임을 나타냅니다(여기에서 양식 컨트롤을 서버에서 가져올 수 있습니다).addcomponent 메서드가 표시되면 Forms Controls를 볼 수 있습니다.이 예에서는 각진 소재를 사용하고 있지 않지만, 동작합니다(@ work를 사용하고 있습니다).이것은 angular 6을 대상으로 하지만 이전 버전에서는 모두 작동합니다.
AngularVersion 5 이상에서는 JITComplierFactory를 추가해야 합니다.
감사해요.
비제이
저도 RC4를 RC5로 업데이트하려면 어떻게 해야 할지 고민하고 있습니다.그래서 우연히 이 엔트리를 발견하게 되었습니다.동적인 컴포넌트 작성에 대한 새로운 접근법은 아직 조금 수수께끼이기 때문에 컴포넌트 팩토리 리졸바에 대해서는 제안하지 않겠습니다.
단, 이 시나리오에서는 컴포넌트 작성에 조금 더 명확한 접근방식을 제안할 수 있습니다.템플릿에서 switch를 사용하면 다음과 같은 조건에 따라 문자열 에디터 또는 텍스트에디터를 작성할 수 있습니다.
<form [ngSwitch]="useTextarea">
<string-editor *ngSwitchCase="false" propertyName="'code'"
[entity]="entity"></string-editor>
<text-editor *ngSwitchCase="true" propertyName="'code'"
[entity]="entity"></text-editor>
</form>
덧붙여서 [prop] 식에서 ""는 단방향 데이터 바인딩을 의미하므로 속성을 변수에 바인딩할 필요가 없다는 것을 알고 있는 경우에는 생략할 수도 있습니다.
다이내믹 스트링을 해석하고 셀렉터로 컴포넌트를 로드하는 방법으로서 필요한 경우 ngx-dynamic-hooks 라이브러리도 도움이 됩니다.처음에는 개인 프로젝트의 일환으로 만들었지만, 주변에 그런 것이 없어서 조금 다듬어서 공개했습니다.
일부 tidbid:
- 셀렉터(또는 선택한 다른 패턴)를 사용하여 임의의 컴포넌트를 다이내믹 문자열에 로드할 수 있습니다.
- 입력 및 출력은 일반 템플릿과 동일하게 사용할 수 있습니다.
- 제한 없이 컴포넌트를 네스트할 수 있습니다.
- 부모 컴포넌트에서 동적으로 로드된 컴포넌트로 라이브 데이터를 전달할 수 있습니다(입출력 바인드에도 사용할 수 있습니다).
- 각 콘센트에 탑재할 수 있는 컴포넌트 및 입력/출력을 제어할 수 있습니다.
- 라이브러리는 Angular의 기본 제공 DOMSanitizer를 사용하여 잠재적으로 안전하지 않은 입력에서도 안전하게 사용할 수 있습니다.
특히 여기의 다른 응답처럼 런타임 컴파일러에 의존하지 않습니다.따라서 템플릿 구문을 사용할 수 없습니다.반대로 말하면, 이것은 Ivy와 오래된 템플릿 엔진뿐만 아니라 JiT와 AoT 모드 모두에서 작동하며 일반적으로 훨씬 더 안전하게 사용할 수 있다는 것을 의미합니다.
이 Stackblitz에서 실제로 볼 수 있습니다.
Ophir Stern의 답변 위에 구축한 이 모델은 Angular 4의 AoT와 함께 작동합니다.유일한 문제는 Dynamic Component에 서비스를 삽입할 수 없다는 것입니다.다만, 이 문제는 감수할 수 있습니다.
주의: Angular 5에서는 테스트하지 않았습니다.
import { Component, OnInit, Input, NgModule, NgModuleFactory, Compiler, EventEmitter, Output } from '@angular/core';
import { JitCompilerFactory } from '@angular/compiler';
export function createJitCompiler() {
return new JitCompilerFactory([{
useDebug: false,
useJit: true
}]).createCompiler();
}
type Bindings = {
[key: string]: any;
};
@Component({
selector: 'app-compile',
template: `
<div *ngIf="dynamicComponent && dynamicModule">
<ng-container *ngComponentOutlet="dynamicComponent; ngModuleFactory: dynamicModule;">
</ng-container>
</div>
`,
styleUrls: ['./compile.component.scss'],
providers: [{provide: Compiler, useFactory: createJitCompiler}]
})
export class CompileComponent implements OnInit {
public dynamicComponent: any;
public dynamicModule: NgModuleFactory<any>;
@Input()
public bindings: Bindings = {};
@Input()
public template: string = '';
constructor(private compiler: Compiler) { }
public ngOnInit() {
try {
this.loadDynamicContent();
} catch (err) {
console.log('Error during template parsing: ', err);
}
}
private loadDynamicContent(): void {
this.dynamicComponent = this.createNewComponent(this.template, this.bindings);
this.dynamicModule = this.compiler.compileModuleSync(this.createComponentModule(this.dynamicComponent));
}
private createComponentModule(componentType: any): any {
const runtimeComponentModule = NgModule({
imports: [],
declarations: [
componentType
],
entryComponents: [componentType]
})(class RuntimeComponentModule { });
return runtimeComponentModule;
}
private createNewComponent(template: string, bindings: Bindings): any {
const dynamicComponent = Component({
selector: 'app-dynamic-component',
template: template
})(class DynamicComponent implements OnInit {
public bindings: Bindings;
constructor() { }
public ngOnInit() {
this.bindings = bindings;
}
});
return dynamicComponent;
}
}
이게 도움이 됐으면 좋겠다.
건배!
이 경우에는 명령어를 사용하여 컴포넌트를 동적으로 작성하는 것이 좋습니다.예:
구성 요소를 생성할 HTML
<ng-container dynamicComponentDirective [someConfig]="someConfig"></ng-container>
나는 다음과 같은 방식으로 지시를 접근하고 설계할 것이다.
const components: {[type: string]: Type<YourConfig>} = {
text : TextEditorComponent,
numeric: NumericComponent,
string: StringEditorComponent,
date: DateComponent,
........
.........
};
@Directive({
selector: '[dynamicComponentDirective]'
})
export class DynamicComponentDirective implements YourConfig, OnChanges, OnInit {
@Input() yourConfig: Define your config here //;
component: ComponentRef<YourConfig>;
constructor(
private resolver: ComponentFactoryResolver,
private container: ViewContainerRef
) {}
ngOnChanges() {
if (this.component) {
this.component.instance.config = this.config;
// config is your config, what evermeta data you want to pass to the component created.
}
}
ngOnInit() {
if (!components[this.config.type]) {
const supportedTypes = Object.keys(components).join(', ');
console.error(`Trying to use an unsupported type ${this.config.type} Supported types: ${supportedTypes}`);
}
const component = this.resolver.resolveComponentFactory<yourConfig>(components[this.config.type]);
this.component = this.container.createComponent(component);
this.component.instance.config = this.config;
}
}
등 - 내의 .ng-container요소를 사용할 수 있습니다.
「」은,yourConfig는, 같게 해 메타데이터를 정의할 수 있습니다.
설정 또는 입력 유형에 따라 디렉티브가 적절하게 동작해야 하며 지원되는 유형에서 적절한 컴포넌트를 렌더링합니다.그렇지 않으면 오류가 기록됩니다.
언급URL : https://stackoverflow.com/questions/38888008/how-can-i-use-create-dynamic-template-to-compile-dynamic-component-with-angular
'programing' 카테고리의 다른 글
| 각도 및 활자 문자:이름을 찾을 수 없음 - 오류: 이름을 찾을 수 없습니다. (0) | 2023.03.11 |
|---|---|
| ASP에서의 Access-Control-Allow-Origin 설정.Net MVC - 가장 간단한 방법 (0) | 2023.03.11 |
| WordPress API의 데이터 설정 해제(wp-json) (0) | 2023.03.11 |
| 플러그인을 사용하지 않고 워드프레스 편집기에서 테이블 옵션 사용 (0) | 2023.03.11 |
| React.js에서 지연을 추가하는 방법 (0) | 2023.03.11 |