Ich möchte dynamisch eine Vorlage erstellen. Dies sollte verwendet werden, um eine ComponentType
zur Laufzeit zu erstellen und sie irgendwo innerhalb der Hosting-Komponente zu platzieren (sogar zu ersetzen) .
Bis ich RC4 verwendet habe ComponentResolver
, aber mit RC5 erhalte ich die folgende Meldung:
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.
Ich habe dieses Dokument gefunden ( Angular 2 Synchronous Dynamic Component Creation )
Und verstehe, dass ich beides verwenden kann
- Art von Dynamik
ngIf
mitComponentFactoryResolver
. Wenn ich bekannte Komponenten innerhalb von übergebe@Component({entryComponents: [comp1, comp2], ...})
- kann ich verwenden.resolveComponentFactory(componentToRender);
- Echte Laufzeitkompilierung mit
Compiler
...
Aber die Frage ist, wie man das benutzt Compiler
? Der obige Hinweis besagt, dass ich anrufen sollte: Compiler.compileComponentSync/Async
- wie?
Beispielsweise. Ich möchte (basierend auf einigen Konfigurationsbedingungen) diese Art von Vorlage für eine Art von Einstellungen erstellen
<form>
<string-editor
[propertyName]="'code'"
[entity]="entity"
></string-editor>
<string-editor
[propertyName]="'description'"
[entity]="entity"
></string-editor>
...
und in einem anderen Fall dieser ( string-editor
wird ersetzt durch text-editor
)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
Und so weiter (unterschiedliche Anzahl / Datum / Referenz editors
nach Eigenschaftstypen, einige Eigenschaften für einige Benutzer übersprungen ...) . Dies ist ein Beispiel, die reale Konfiguration könnte viel unterschiedlichere und komplexere Vorlagen erzeugen.
Die Vorlage ändert sich, daher kann ich vorhandene nicht verwenden ComponentFactoryResolver
und übergeben ... Ich benötige eine Lösung mit der Compiler
.
quelle
$compile
diese Methoden tatsächlich nicht können - ich erstelle eine Anwendung, in der ich nur den HTML-Code kompilieren möchte, der über die Seite eines Drittanbieters und Ajax-Aufrufe eingeht. Ich kann den HTML-Code nicht von der Seite entfernen und in meine eigene Vorlage einfügen. SeufzAntworten:
BEARBEITEN - bezogen auf 2.3.0 (07.12.2016)
Ein ähnliches Thema wird hier behandelt. Entspricht $ compile in Angular 2 . Wir müssen
JitCompiler
und verwendenNgModule
. LesenNgModule
Sie hier mehr über Angular2:In einer Nussschale
Es gibt einen funktionierenden Plunker / ein funktionierendes Beispiel (dynamische Vorlage, dynamischer Komponententyp, dynamisches Modul,
JitCompiler
, ... in Aktion)Das Prinzip ist:
1) erstellen Template
2) findet
ComponentFactory
im Cache - gehen zu 7)3) - erstellen
Component
4) - erstellen
Module
5) - Kompilierung
Module
6) - Rückkehr (und Cache für eine spätere Verwendung)
ComponentFactory
7) Verwendung Ziel und
ComponentFactory
eine Instanz zu erstellen von dynamischComponent
Hier ist ein Code-Snippet (mehr davon hier ) - Unser benutzerdefinierter Builder gibt gerade erstellt / zwischengespeichert zurück
ComponentFactory
und die Ansicht Zielplatzhalter verbrauchen, um eine Instanz von zu erstellenDynamicComponent
Das ist es - kurz gesagt. Um mehr Details zu erhalten, lesen Sie unten
.
TL & DR
Beobachten Sie einen Plunker und lesen Sie die Details, falls ein Ausschnitt weitere Erklärungen erfordert
.
Detaillierte Erklärung - Angular2 RC6 ++ & Laufzeitkomponenten
Nachfolgend werden wir dieses Szenario beschreiben
PartsModule:NgModule
(Halter von kleinen Stücken)DynamicModule:NgModule
, das unsere dynamische Komponente enthält (undPartsModule
dynamisch referenziert ).Component
Typ erstellen (nur wenn sich die Vorlage geändert hat)RuntimeModule:NgModule
. Dieses Modul enthält den zuvor erstelltenComponent
TypJitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
zu bekommenComponentFactory
DynamicComponent
Jobs - des Platzhalters "Ziel anzeigen" undComponentFactory
@Inputs
um neue Instanz (Schalter ausINPUT
zuTEXTAREA
bearbeiten) , verbrauchen@Outputs
NgModule
Wir brauchen ein
NgModule
s.Es wird ein Modul für alle Kleinteile, zum Beispiel
string-editor
,text-editor
(date-editor
,number-editor
...)Das zweite wird ein Modul für unser dynamisches Handling sein. Es wird Hosting-Komponenten und einige Anbieter enthalten. Dies werden Singletons sein. Dafür werden wir sie standardmäßig veröffentlichen - mit
forRoot()
Schließlich benötigen wir ein Ad-hoc-Laufzeitmodul. Dieses wird jedoch später als Teil des
DynamicTypeBuilder
Jobs erstellt.Das vierte Modul, das Anwendungsmodul, ist dasjenige, das Compiler-Anbieter deklariert:
Lesen Sie viel mehr über NgModule es:
EIN Template Builder
In unserem Beispiel werden wir Details dieser Art von Entität verarbeiten
Um ein zu erstellen
template
, verwenden wir in diesem Plunker diesen einfachen / naiven Builder.Ein Trick dabei ist - es erstellt eine Vorlage, die eine Reihe bekannter Eigenschaften verwendet, z
entity
. Solche Eigenschaften müssen Teil einer dynamischen Komponente sein, die wir als nächstes erstellen werden.Um es ein bisschen einfacher zu machen, können wir eine Schnittstelle verwenden, um Eigenschaften zu definieren, die unser Template Builder verwenden kann. Dies wird durch unseren dynamischen Komponententyp implementiert.
Ein
ComponentFactory
BaumeisterSehr wichtig ist hierbei Folgendes zu beachten:
Wir berühren also den Kern unserer Lösung. Der Builder wird 1) erstellen
ComponentType
2) erstellenNgModule
3) kompilierenComponentFactory
4) zwischenspeichern für die spätere Wiederverwendung.Eine Abhängigkeit, die wir erhalten müssen:
Und hier ist ein Ausschnitt, wie man ein
ComponentFactory
:Und hier sind zwei Methoden, die die wirklich coole Art darstellen, dekorierte Klassen / Typen zur Laufzeit zu erstellen . Nicht nur,
@Component
sondern auch die@NgModule
Wichtig:
ComponentFactory
wird von der Hosting-Komponente verwendetDas letzte Stück ist eine Komponente, die das Ziel für unsere dynamische Komponente enthält, z
<div #dynamicContentPlaceHolder></div>
. Wir erhalten einen Verweis darauf undComponentFactory
erstellen damit eine Komponente. Das ist auf den Punkt gebracht, und hier sind alle Teile dieser Komponente (falls erforderlich, öffnen Sie hier den Plunker ).Fassen wir zunächst die Importanweisungen zusammen:
Wir erhalten nur Builder für Vorlagen und Komponenten. Als nächstes folgen die Eigenschaften, die für unser Beispiel benötigt werden (mehr in den Kommentaren).
In diesem einfachen Szenario hat unsere Hosting-Komponente keine
@Input
. Es muss also nicht auf Änderungen reagieren. Aber trotz dieser Tatsache (und um auf kommende Änderungen vorbereitet zu sein) - müssen wir ein Flag einführen, wenn die Komponente bereits war (erstens) initiiert wurde. Und nur dann können wir die Magie beginnen.Schließlich werden wir unseren Komponenten-Builder verwenden, der gerade kompiliert / zwischengespeichert wurde
ComponentFacotry
. Unser Zielplatzhalter wird gebeten, dasComponent
mit dieser Fabrik zu instanziieren .kleine Erweiterung
Außerdem müssen wir einen Verweis auf die kompilierte Vorlage behalten, um sie ordnungsgemäß zu verwenden
destroy()
, wann immer wir sie ändern werden.getan
Das ist so ziemlich alles. Vergessen Sie nicht, alles zu zerstören , was dynamisch erstellt wurde (ngOnDestroy) . Auch sicher sein , zu Cache dynamisch
types
undmodules
wenn der einzige Unterschied ist ihre Vorlage.Überprüfen Sie sie alle in Aktion hier
quelle
type.builder.ts
wie Sie es gezeigt haben, wünschte ich mir, dass jeder Benutzer verstehen würde, wie man das alles einfügt Kontext ... Hoffe, es könnte nützlich sein;)BEARBEITEN (26.08.2017) : Die folgende Lösung funktioniert gut mit Angular2 und 4. Ich habe sie aktualisiert, um eine Vorlagenvariable und einen Klick-Handler zu enthalten, und sie mit Angular 4.3 getestet.
Für Angular4 ist ngComponentOutlet, wie in Ophirs Antwort beschrieben, eine viel bessere Lösung. Derzeit werden jedoch keine Ein- und Ausgänge unterstützt . Wenn [diese PR] ( https://github.com/angular/angular/pull/15362] akzeptiert wird, ist dies über die vom create-Ereignis zurückgegebene Komponenteninstanz möglich.
Ng-dynamic-component möglicherweise die beste und einfachste Lösung insgesamt, aber ich habe das noch nicht getestet.
Die Antwort von @Long Field ist genau richtig! Hier ist ein weiteres (synchrones) Beispiel:
Live unter http://plnkr.co/edit/fdP9Oc .
quelle
ngAfterViewInit
Aufruf mit einemconst template
funktioniert nicht. Aber wenn Ihre Aufgabe darin bestand, den oben beschriebenen Ansatz zu reduzieren (Vorlage erstellen, Komponente erstellen, Modul erstellen, kompilieren, Factory erstellen ... Instanz erstellen) ... haben Sie es wahrscheinlich getanIch muss spät auf der Party angekommen sein, keine der Lösungen hier schien mir hilfreich zu sein - zu chaotisch und fühlte sich wie eine zu große Problemumgehung an.
Was ich am Ende ist zu tun mit
Angular 4.0.0-beta.6
‚s ngComponentOutlet .Dies gab mir die kürzeste und einfachste Lösung, die alle in die Datei der dynamischen Komponente geschrieben wurden.
my-component
- die Komponente, in der eine dynamische Komponente gerendert wirdDynamicComponent
- Die Komponente, die dynamisch erstellt werden soll und in meiner Komponente gerendert wirdVergessen Sie nicht, alle Winkelbibliotheken auf ^ Angular 4.0.0 zu aktualisieren
Hoffe das hilft, viel Glück!
AKTUALISIEREN
Funktioniert auch für Winkel 5.
quelle
2019 Juni Antwort
Großartige Neuigkeiten! Es scheint, dass das @ angle / cdk- Paket jetzt erstklassige Unterstützung für Portale bietet !
Zum Zeitpunkt des Schreibens fand ich die oben genannten offiziellen Dokumente nicht besonders hilfreich (insbesondere im Hinblick auf das Senden von Daten und das Empfangen von Ereignissen von den dynamischen Komponenten). Zusammenfassend müssen Sie:
Schritt 1) Aktualisieren Sie Ihre
AppModule
Importieren Sie
PortalModule
aus dem@angular/cdk/portal
Paket und registrieren Sie Ihre dynamischen Komponenten darinentryComponents
Schritt 2. Option A: Wenn Sie KEINE Daten an Ihre dynamischen Komponenten übergeben und Ereignisse von diesen empfangen müssen :
Sehen Sie es in Aktion
Schritt 2. Option B: Wenn Sie Daten an Ihre dynamischen Komponenten übergeben und Ereignisse von diesen empfangen müssen :
Sehen Sie es in Aktion
quelle
Portal
Ansatz vonngTemplateOutlet
undngComponentOutlet
? 🤔Ich beschloss, alles, was ich gelernt hatte, in eine Datei zu komprimieren . Hier gibt es besonders im Vergleich zu vor RC5 viel zu beachten. Beachten Sie, dass diese Quelldatei AppModule und AppComponent enthält.
quelle
Ich habe ein einfaches Beispiel, um zu zeigen, wie man eine dynamische 2-rc6-Komponente mit Winkel erstellt.
Angenommen, Sie haben eine dynamische HTML-Vorlage = template1 und möchten dynamisch laden, indem Sie zunächst eine Komponente einbinden
hier kann template1 als html die ng2-komponente enthalten
Ab rc6 muss @NgModule diese Komponente umbrechen. @NgModule, genau wie das Modul in anglarJS 1, entkoppelt es verschiedene Teile der ng2-Anwendung, also:
(Importieren Sie hier RouterModule, da in meinem Beispiel einige Routenkomponenten in meinem HTML-Code enthalten sind, wie Sie später sehen können.)
Jetzt können Sie DynamicModule wie folgt kompilieren:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
Und wir müssen oben in app.moudule.ts setzen, um es zu laden, siehe meine app.moudle.ts. Weitere und ausführlichere Informationen finden Sie unter: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts und app.moudle.ts
und siehe Demo: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview
quelle
In Winkel 7.x habe ich dafür Winkelelemente verwendet.
Installieren Sie @ angle-elements npm i @ angle / elements -s
Zubehörservice erstellen.
Beachten Sie, dass Ihr benutzerdefiniertes Element-Tag mit dem Winkelkomponenten-Selektor unterschiedlich sein muss. in AppUserIconComponent:
und in diesem Fall habe ich "Benutzer-Symbol" verwendet.
oder so:
(in Vorlage):
Beachten Sie, dass Sie im zweiten Fall Objekte mit JSON.stringify übergeben und anschließend erneut analysieren müssen. Ich kann keine bessere Lösung finden.
quelle
document.createElement(tagName);
Dies wurde in der endgültigen Version von Angular 2 einfach mithilfe der Direktive dynamicComponent von ng-dynamic behoben .
Verwendung:
Wo Vorlage Ihre dynamische Vorlage ist und der Kontext auf ein beliebiges dynamisches Datenmodell festgelegt werden kann, an das Ihre Vorlage gebunden werden soll.
quelle
Ich möchte ein paar Details zu diesem sehr ausgezeichneten Beitrag von Radim hinzufügen.
Ich nahm diese Lösung und arbeitete ein wenig daran und stieß schnell auf einige Einschränkungen. Ich werde diese nur skizzieren und dann auch die Lösung dafür geben.
Auf der Grundlage dieses Beitrags habe ich eine weitere Frage gestellt, wie diese Einschränkungen erreicht werden können. Diese finden Sie hier:
rekursive dynamische Vorlagenkompilierung in angle2
Ich werde nur die Antworten auf diese Einschränkungen skizzieren, falls Sie auf dasselbe Problem wie ich stoßen, da dies die Lösung sehr flexibler macht. Es wäre fantastisch, wenn auch der erste Plunker damit aktualisiert würde.
Um das Verschachteln von dynamischen Details ineinander zu aktivieren, müssen Sie DynamicModule.forRoot () in der import-Anweisung in type.builder.ts hinzufügen
Außerdem war es nicht möglich zu verwenden
<dynamic-detail>
innerhalb eines der Teile String-Editor oder Text-Editor zu verwenden.Um dies zu aktivieren, müssen Sie
parts.module.ts
und änderndynamic.module.ts
Innerhalb
parts.module.ts
Sie müssen hinzufügenDynamicDetail
in derDYNAMIC_DIRECTIVES
Außerdem
dynamic.module.ts
müssten Sie das dynamicDetail entfernen, da sie jetzt Teil der Teile sindEin funktionierender modifizierter Plunker finden Sie hier: http://plnkr.co/edit/UYnQHF?p=preview (Ich habe dieses Problem nicht gelöst, ich bin nur der Messenger :-D)
Schließlich war es nicht möglich, Templateurls in den Teilen zu verwenden, die auf den dynamischen Komponenten erstellt wurden. Eine Lösung (oder Problemumgehung. Ich bin nicht sicher, ob es sich um einen Winkelfehler oder eine falsche Verwendung des Frameworks handelt) bestand darin, einen Compiler im Konstruktor zu erstellen, anstatt ihn zu injizieren.
Verwenden Sie dann die
_compiler
zum Kompilieren, dann werden auch templateUrls aktiviert.Hoffe das hilft jemand anderem!
Viele Grüße Morten
quelle
Nach Radmins hervorragender Antwort ist für alle, die Angular-Cli Version 1.0.0-Beta.22 und höher verwenden, eine kleine Optimierung erforderlich.
COMPILER_PROVIDERS
kann nicht mehr importiert werden (Details siehe angle-cli GitHub ).Die Problemumgehung besteht also darin, nicht
COMPILER_PROVIDERS
undJitCompiler
improviders
Abschnitt überhaupt zu verwenden, sondernJitCompilerFactory
von '@ angle / compiler' stattdessen wie folgt innerhalb der Type Builder-Klasse zu verwenden:Wie Sie sehen können, ist es nicht injizierbar und hat daher keine Abhängigkeiten vom DI. Diese Lösung sollte auch für Projekte funktionieren, die Angular-Cli nicht verwenden.
quelle
Ich selbst versuche zu sehen, wie ich RC4 auf RC5 aktualisieren kann, und bin daher auf diesen Eintrag gestoßen, und der neue Ansatz zur dynamischen Komponentenerstellung ist mir immer noch ein Rätsel, daher schlage ich nichts zum Komponenten-Factory-Resolver vor.
Was ich jedoch vorschlagen kann, ist ein etwas klarerer Ansatz für die Erstellung von Komponenten in diesem Szenario. Verwenden Sie einfach den Schalter in der Vorlage, mit dem unter bestimmten Bedingungen wie folgt ein Zeichenfolgen- oder Texteditor erstellt wird:
Übrigens hat "[" im Ausdruck [prop] eine Bedeutung, dies zeigt eine Einweg-Datenbindung an. Daher können und sollten Sie diese weglassen, falls Sie wissen, dass Sie die Eigenschaft nicht an eine Variable binden müssen.
quelle
switch
/case
nur wenige Entscheidungen enthält. Stellen Sie sich jedoch vor, dass die generierte Vorlage sehr groß sein könnte ... und sich für jede Entität, je nach Sicherheit, je nach Entitätsstatus und jedem Eigenschaftstyp (Nummer, Datum, Referenz ... Editoren) unterscheiden könnte. Wenn Sie dies in einer HTML-Vorlage mit lösenngSwitch
, wird eine große, sehr, sehr großehtml
Datei erstellt.Dies ist das Beispiel für dynamische Formularsteuerelemente, die vom Server generiert werden.
https://stackblitz.com/edit/angular-t3mmg6
In diesem Beispiel handelt es sich um dynamische Formularsteuerelemente, die in der Komponente hinzufügen enthalten sind (hier können Sie die Formularsteuerelemente vom Server abrufen). Wenn die Methode addcomponent angezeigt wird, werden die Formularsteuerelemente angezeigt. In diesem Beispiel verwende ich kein eckiges Material, aber es funktioniert (ich verwende @ work). Dies ist das Ziel von Winkel 6, funktioniert jedoch in allen vorherigen Versionen.
JITComplierFactory für AngularVersion 5 und höher muss hinzugefügt werden.
Vielen Dank
Vijay
quelle
In diesem speziellen Fall wäre die Verwendung einer Direktive zum dynamischen Erstellen der Komponente eine bessere Option. Beispiel:
In dem HTML, in dem Sie die Komponente erstellen möchten
Ich würde die Richtlinie folgendermaßen angehen und gestalten.
Also in Ihren Komponenten Text, Zeichenfolge, Datum, was auch immer - unabhängig von der Konfiguration, die Sie im HTML in der übergeben haben
ng-container
Element übergeben haben, wäre verfügbar.Die Konfiguration,
yourConfig
kann identisch sein und Ihre Metadaten definieren.Abhängig von Ihrer Konfiguration oder Ihrem Eingabetyp sollte die Direktive entsprechend handeln und von den unterstützten Typen die entsprechende Komponente rendern. Wenn nicht, wird ein Fehler protokolliert.
quelle
Aufbauend auf der Antwort von Ophir Stern ist hier eine Variante, die mit AoT in Angular 4 funktioniert. Das einzige Problem, das ich habe, ist, dass ich keine Dienste in die DynamicComponent einfügen kann, aber damit kann ich leben.
Hinweis: Ich habe nicht mit Angular 5 getestet.
Hoffe das hilft.
Prost!
quelle