EDIT - 2.3.0 (2016-12-07) ile ilgili
NOT: önceki sürüm için çözüm bulmak için bu gönderinin geçmişini kontrol edin
Benzer konu Angular 2'de $ compile eşdeğeri burada tartışılmaktadır . Biz kullanmak gerekir JitCompiler
ve NgModule
. NgModule
Angular2 ile ilgili daha fazla bilgiyi buradan edinebilirsiniz:
Kısaca
Orada çalışan bir plunker / example (dinamik şablon, dinamik bileşen tipi, dinamik modül, JitCompiler
... eylemde)
Temel öğe:
1) Şablon
2 oluşturma )ComponentFactory
önbellekte bulma - 7'ye git )
3) - oluşturma Component
4) - oluşturma Module
5) - derleme Module
6) - dönüş (ve daha sonra kullanmak üzere önbellek) ComponentFactory
7) Hedef'i kullanma ve ComponentFactory
bir Örnek oluşturma dinamikComponent
İşte bir kod snippet'i (daha fazlası burada ) - Özel Oluşturucumuz yeni oluşturulmuş / önbelleğe alınmış olarak dönüyor ComponentFactory
ve Hedef yer tutucusunun görünümü,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;
//...
});
İşte bu - kısaca. Daha fazla bilgi almak için .. aşağıyı okuyun
.
TP & DR
Bir pasajı gözlemleyin ve bazı pasajların daha fazla açıklama gerektirmesi durumunda ayrıntıları okumak için geri dönün
.
Ayrıntılı açıklama - Angular2 RC6 ++ ve çalışma zamanı bileşenleri
Bu senaryonun açıklamasının altında ,
- bir modül oluşturun
PartsModule:NgModule
(küçük parçaların tutucusu)
DynamicModule:NgModule
Dinamik bileşenimizi içerecek başka bir modül oluşturun (ve PartsModule
dinamik olarak referans verin )
- dinamik Şablon oluşturma (basit yaklaşım)
- yeni
Component
tür oluştur (yalnızca şablon değiştiyse)
- yeni oluştur
RuntimeModule:NgModule
. Bu modül önceden oluşturulan Component
türü içerecektir
JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
almak için çağırComponentFactory
DynamicComponent
Hedefi Görüntüle yer tutucusunun işinin bir örneğini oluşturmak veComponentFactory
- atamak
@Inputs
için yeni bir örneği (geçişten INPUT
için TEXTAREA
düzenleme) , tüketmek@Outputs
NgModule
Bir NgModule
s lazım .
Çok basit bir örnek göstermek isterken , bu durumda, üç modüle ihtiyacım olacaktı (aslında 4 - ama AppModule'u saymıyorum) . Lütfen, gerçekten sağlam bir dinamik bileşen üreticisi için basit bir snippet yerine bunu alın .
Tüm küçük bileşenler için bir modül olacaktır , örneğin string-editor
, text-editor
( date-editor
, number-editor
...)
@NgModule({
imports: [
CommonModule,
FormsModule
],
declarations: [
DYNAMIC_DIRECTIVES
],
exports: [
DYNAMIC_DIRECTIVES,
CommonModule,
FormsModule
]
})
export class PartsModule { }
Nerede DYNAMIC_DIRECTIVES
genişletilebilir ve dinamik Bileşen şablon / türü için kullanılan tüm küçük parçaları tutmak için tasarlanmıştır. Uygulamayı / parçaları / parçaları kontrol edin.
İkincisi, Dinamik malzeme işleme için modül olacak. Bu hosting bileşenleri ve singleton olacak bazı sağlayıcıları içerecektir. Bu nedenle onları standart bir şekilde yayınlayacağız -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
],
};
}
}
İçindeki kullanımını kontrol edin forRoot()
.AppModule
Son olarak, bir adhoc, runtime modülüne ihtiyacımız olacak ... ama bu daha sonra DynamicTypeBuilder
işin bir parçası olarak yaratılacak .
Dördüncü modül, uygulama modülü derleyici sağlayıcılarını beyan eden modüldür:
...
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 hakkında daha fazlasını okuyun (okuyun) :
Bir şablon oluşturucu
Örneğimizde bu tür varlıkların detaylarını işleyeceğiz
entity = {
code: "ABC123",
description: "A description of this Entity"
};
Bir oluşturmak için template
, bu dalgıçta bu basit / saf oluşturucuyu kullanıyoruz.
Gerçek çözüm, gerçek bir şablon oluşturucu, uygulamanızın çok şey yapabileceği yerdir
// 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>";
}
}
Burada bir hile - bilinen bazı özellikleri kullanan bir şablon oluşturur, örn entity
. Bu tür özellikler (-ies), bir sonraki oluşturacağımız dinamik bileşenin bir parçası olmalıdır.
Bunu biraz daha kolaylaştırmak için Şablon oluşturucumuzun kullanabileceği özellikleri tanımlamak için bir arayüz kullanabiliriz. Bu, dinamik Bileşen tipimiz tarafından uygulanacaktır.
export interface IHaveDynamicData {
public entity: any;
...
}
Bir ComponentFactory
inşaatçı
Burada çok önemli olan şey akılda tutmaktır:
bileşen türümüz, bizim ürünümüzle birlikte oluşturulabilir DynamicTypeBuilder
, ancak yalnızca şablonu (yukarıda oluşturulmuştur) ile farklılık gösterebilir . Bileşenlerin özellikleri (girişler, çıkışlar veya bazıları korumalı) hala aynıdır. Farklı özelliklere ihtiyacımız varsa, Şablon ve Tür Oluşturucu'nun farklı kombinasyonunu tanımlamalıyız
Yani, çözümümüzün özüne dokunuyoruz. Oluşturucu, 1) oluşturur ComponentType
2) NgModule
3) derler ComponentFactory
4) daha sonra yeniden kullanmak için önbellek .
Almak zorunda olduğumuz bir bağımlılık:
// plunker - app/dynamic/type.builder.ts
import { JitCompiler } from '@angular/compiler';
@Injectable()
export class DynamicTypeBuilder {
// wee need Dynamic component builder
constructor(
protected compiler: JitCompiler
) {}
Ve işte bir pasaj nasıl elde edilir 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);
});
});
}
Yukarıda hem ve hem de önbellek oluşturuyoruz . Çünkü şablon (aslında hepsinin gerçek dinamik kısmı) aynı ise .. tekrar kullanabilirizComponent
Module
Ve burada, çalışma zamanında süslü sınıflar / türler oluşturmanın gerçekten harika yolunu temsil eden iki yöntem var . Sadece @Component
değil, aynı zamanda@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;
}
Önemli:
bileşen dinamik türlerimiz yalnızca şablondan farklılık gösterir. Bu gerçeği önbelleklemek için kullanıyoruz. Bu gerçekten çok önemli. Angular2 de bunları önbelleğe alır .. türüne göre . Ve aynı şablon dizeleri için yeni türler yaratırsak ... bellek sızıntıları üretmeye başlayacağız.
ComponentFactory
barındırma bileşeni tarafından kullanılır
Son parça, dinamik bileşenimiz için hedefi barındıran bir bileşendir, örn <div #dynamicContentPlaceHolder></div>
. Bir referans alıyoruz ve ComponentFactory
bir bileşen oluşturmak için kullanıyoruz . Kısacası ve işte bu bileşenin tüm parçaları (gerekirse, burada açık dalgıç) )
Öncelikle ithalat beyanlarını özetleyelim:
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
) {}
...
Sadece şablon ve bileşen oluşturucuları alıyoruz. Sonraki örnek için gerekli özellikler (yorumlarda daha fazla)
// 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"
};
Bu basit senaryoda, barındırma bileşenimiz yoktur @Input
. Dolayısıyla değişikliklere tepki vermek zorunda değildir. Ancak bu gerçeğe rağmen (ve gelecekteki değişikliklere hazır olmak için) - bileşen zaten varsa (ilk olarak) bazı bayraklar eklemeliyiz başlatılmışsa eklememiz gerekir. Ve ancak o zaman büyüye başlayabiliriz.
Sonunda bileşen oluşturucumuzu kullanacağız ve sadece derlendi / önbelleğe alındı ComponentFacotry
. Bizim Hedef tutucu örneğini istenecek o fabrika.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;
//...
});
}
küçük uzatma
Ayrıca, düzgün bir şekilde yapabilmek için derlediğimiz şablona bir referans göndermemiz destroy()
gerekir.
// 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;
}
}
tamam
Hemen hemen bu kadar. Dinamik olarak inşa edilmiş herhangi bir şeyi yok etmeyi unutmayın (ngOnDestroy) . Ayrıca, dinamik önbelleğe aldığınızdan emin olun types
ve modules
tek fark şablonuysa.
Hepsini burada kontrol edin
bu yayının önceki sürümlerini (ör. RC5 ile ilgili) görmek için geçmişi kontrol edin