EDIT - 2.3.0 (2016-12-07) से संबंधित
नोट: पिछले संस्करण का समाधान पाने के लिए, इस पोस्ट के इतिहास की जाँच करें
इसी तरह के विषय पर यहां चर्चा की गई है समतुल्य $ संकलन का कोणीय 2 में । हम उपयोग करने की आवश्यकता 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;
//...
});
यह वह है - संक्षेप में। अधिक विवरण प्राप्त करने के लिए .. नीचे पढ़ें
।
टी एल एंड डॉ
एक प्लंकर का निरीक्षण करें और कुछ स्निपेट के स्पष्टीकरण के मामले में विवरण पढ़ने के लिए वापस आएं
।
विस्तृत विवरण - Angular2 RC6 ++ और रनटाइम घटक
इस परिदृश्य के विवरण के नीचे , हम करेंगे
- एक मॉड्यूल बनाएं
PartsModule:NgModule
(छोटे टुकड़ों का धारक)
- एक और मॉड्यूल बनाएं
DynamicModule:NgModule
, जिसमें हमारा गतिशील घटक होगा (और PartsModule
गतिशील रूप से संदर्भ )
- गतिशील टेम्पलेट बनाएँ (सरल दृष्टिकोण)
- नया
Component
प्रकार बनाएं (केवल अगर टेम्पलेट बदल गया है)
- नया बनाएँ
RuntimeModule:NgModule
। इस मॉड्यूल में पहले से बनाया गया Component
प्रकार होगा
JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
पाने के लिए कॉल करेंComponentFactory
DynamicComponent
टारगेट प्लेसहोल्डर के - जॉब का एक उदाहरण बनाएं औरComponentFactory
- आवंटित
@Inputs
करने के लिए नया उदाहरण (से स्विच INPUT
करने के लिए TEXTAREA
संपादन) , उपभोग@Outputs
NgModule
हमें एक 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 { }
जहां DYNAMIC_DIRECTIVES
एक्स्टेंसिबल हैं और हमारे गतिशील घटक टेम्पलेट / प्रकार के लिए उपयोग किए जाने वाले सभी छोटे भागों को रखने का इरादा है। एप्लिकेशन / भागों / 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
],
};
}
}
के उपयोग की जांच 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
। ऐसी संपत्ति (-एएस) को गतिशील घटक का हिस्सा होना चाहिए, जिसे हम आगे बनाएंगे।
इसे थोड़ा और आसान बनाने के लिए, हम गुणों को परिभाषित करने के लिए एक इंटरफ़ेस का उपयोग कर सकते हैं, जिसे हमारे टेम्पलेट बिल्डर उपयोग कर सकते हैं। यह हमारे गतिशील घटक प्रकार द्वारा कार्यान्वित किया जाएगा।
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
एक घटक बनाने के लिए उपयोग किया जाता है। यह संक्षेप में है, और यहां उस घटक के सभी टुकड़े हैं (यदि आवश्यक हो, तो यहां प्लंकर खोलें )
आइए सबसे पहले आयात विवरणों को संक्षेप में प्रस्तुत करें:
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 संबंधित) को देखने के लिए, इतिहास की जाँच करें