Chcę dynamicznie utworzyć szablon. Powinno to zostać wykorzystane do zbudowania ComponentType
środowiska wykonawczego i umieszczenia go (a nawet zastąpienia) gdzieś w komponencie hostingowym.
Do czasu używania RC4 ComponentResolver
, ale wraz z RC5 pojawia się następujący komunikat:
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.
Znalazłem ten dokument ( Synchroniczne dynamiczne tworzenie komponentów Angular 2 )
I zrozum, że mogę użyć albo
- Rodzaj dynamiki
ngIf
zComponentFactoryResolver
. Jeśli przekażę znane elementy wewnątrz@Component({entryComponents: [comp1, comp2], ...})
- mogę użyć.resolveComponentFactory(componentToRender);
- Kompilacja środowiska wykonawczego z
Compiler
...
Ale pytanie brzmi: jak tego użyć Compiler
? Powyższa uwaga mówi, że powinienem zadzwonić: Compiler.compileComponentSync/Async
- więc jak?
Na przykład. Chcę utworzyć (w oparciu o niektóre warunki konfiguracji) tego rodzaju szablon dla jednego rodzaju ustawień
<form>
<string-editor
[propertyName]="'code'"
[entity]="entity"
></string-editor>
<string-editor
[propertyName]="'description'"
[entity]="entity"
></string-editor>
...
a w innym przypadku ten ( string-editor
jest zastąpiony przez text-editor
)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
I tak dalej (inny numer / data / odniesienie editors
według typów nieruchomości, pomijając niektóre właściwości dla niektórych użytkowników ...) . tzn. jest to przykład, rzeczywista konfiguracja może wygenerować znacznie więcej różnych i złożonych szablonów.
Szablon się zmienia, więc nie mogę używać ComponentFactoryResolver
i przekazywać istniejących ... Potrzebuję rozwiązania z Compiler
.
źródło
$compile
właściwie może zrobić to, czego nie potrafią te metody - tworzę aplikację, w której chcę po prostu skompilować kod HTML przychodzący za pośrednictwem strony innej firmy i wywołań ajax. Nie mogę usunąć kodu HTML ze strony i umieścić go we własnym szablonie. WestchnienieOdpowiedzi:
EDYCJA - powiązane z 2.3.0 (2016-12-07)
Podobny temat omówiono tutaj Odpowiednik kompilacji $ w Angular 2 . Musimy użyć
JitCompiler
iNgModule
. Przeczytaj więcej oNgModule
Angular2 tutaj:W skrócie
Istnieje działający plunker / przykład (szablon dynamiczny, typ komponentu dynamicznego, moduł dynamiczny
JitCompiler
, ... w akcji)Podstawowa zasada to:
1) utwórz szablon
2) znajdź
ComponentFactory
w pamięci podręcznej - przejdź do 7)3) - utwórz
Component
4) - utwórz
Module
5) - skompiluj
Module
6) - zwróć (i pamięć podręczną do późniejszego wykorzystania)
ComponentFactory
7) użyj obiektu docelowego i
ComponentFactory
utwórz instancję dynamicznyComponent
Oto fragment kodu (więcej tutaj ) - nasz niestandardowy konstruktor zwraca właśnie zbudowany / buforowany,
ComponentFactory
a widok Docelowy symbol zastępczy konsumuje się, aby utworzyć instancjęDynamicComponent
To jest to - w skrócie. Aby uzyskać więcej informacji .. przeczytaj poniżej
.
TL&DR
Obserwuj plunkera i wróć, by przeczytać szczegóły, na wypadek gdyby jakiś fragment kodu wymagał więcej wyjaśnień
.
Szczegółowe objaśnienie - Angular2 RC6 ++ i komponenty środowiska wykonawczego
Poniżej opis tego scenariusza , będziemy
PartsModule:NgModule
(uchwyt małych elementów)DynamicModule:NgModule
, który będzie zawierał nasz komponent dynamiczny (i referencjePartsModule
dynamicznie)Component
typ (tylko jeśli szablon się zmienił)RuntimeModule:NgModule
. Ten moduł będzie zawierał wcześniej utworzonyComponent
typJitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
aby uzyskaćComponentFactory
DynamicComponent
zadania zastępczego View Target iComponentFactory
@Inputs
do nowej instancji (z przełącznikiemINPUT
doTEXTAREA
edycji) , zużywają@Outputs
NgModule
Potrzebujemy
NgModule
s.Nie będzie jeden moduł dla wszystkich małych elementów, na przykład
string-editor
,text-editor
(date-editor
,number-editor
...)Drugi będzie modułem do naszej dynamicznej obsługi rzeczy. Będzie zawierał komponenty hostingowe i niektórych dostawców .. które będą singletonami. Dlatego opublikujemy je w standardowy sposób - z
forRoot()
Na koniec będziemy potrzebować adhoc, modułu wykonawczego .. ale zostanie on utworzony później, jako część
DynamicTypeBuilder
zadania.Czwarty moduł, moduł aplikacji, jest tym, który deklaruje dostawców kompilatora:
Przeczytaj (czytaj) dużo więcej o NgModule tam:
Szablon budowniczy
W naszym przykładzie przetworzymy szczegóły tego rodzaju bytu
Aby stworzyć
template
, w tym plunkerze używamy tego prostego / naiwnego budowniczego.Sztuką jest tutaj - buduje szablon, który wykorzystuje pewien zestaw znanych właściwości, np
entity
. Taka właściwość (-y) musi być częścią komponentu dynamicznego, który utworzymy następnie.Aby było trochę łatwiej, możemy użyć interfejsu do zdefiniowania właściwości, których może użyć nasz konstruktor szablonów. Zostanie to zaimplementowane przez nasz dynamiczny typ komponentu.
ComponentFactory
budowniczyBardzo ważne jest, aby pamiętać:
Dotykamy więc rdzenia naszego rozwiązania. Konstruktor: 1) utworzy
ComponentType
2) utworzyNgModule
3) skompilujeComponentFactory
4) buforuje go w celu późniejszego ponownego użycia.Zależność, którą musimy otrzymać:
A oto fragment, w jaki sposób uzyskać
ComponentFactory
:A oto dwie metody, które reprezentują naprawdę fajny sposób tworzenia udekorowanych klas / typów w środowisku wykonawczym. Nie tylko,
@Component
ale także@NgModule
Ważny:
ComponentFactory
używany przez komponent hostingowyOstatnim elementem jest komponent, który hostuje cel dla naszego komponentu dynamicznego, np
<div #dynamicContentPlaceHolder></div>
. Otrzymujemy odniesienie do niego i używamy goComponentFactory
do utworzenia komponentu. To w skrócie, a oto wszystkie elementy tego komponentu (w razie potrzeby otwórz tutaj plunker )Podsumujmy najpierw instrukcje importu:
Właśnie otrzymujemy konstruktorów szablonów i komponentów. Dalej są właściwości, które są potrzebne w naszym przykładzie (więcej w komentarzach)
W tym prostym scenariuszu nasz komponent hostingowy nie ma żadnego
@Input
. Nie musi więc reagować na zmiany. Ale pomimo tego (i aby być gotowym na nadchodzące zmiany) - musimy wprowadzić flagę, jeśli składnik został już (po raz pierwszy) zainicjowany. I dopiero wtedy możemy rozpocząć magię.Wreszcie użyjemy naszego konstruktora komponentów, który jest po prostu skompilowany / buforowany
ComponentFacotry
. Nasz obiekt zastępczy Target zostanie poproszony o utworzenie instancji wComponent
tej fabryce.małe rozszerzenie
Musimy również zachować odniesienie do skompilowanego szablonu .., aby móc
destroy()
go poprawnie , za każdym razem, gdy go zmienimy.Gotowe
To właściwie wszystko. Nie zapomnij zniszczyć czegokolwiek, co zostało zbudowane dynamicznie (ngOnDestroy) . Pamiętaj też o buforowaniu dynamicznym
types
imodules
jeśli jedyną różnicą jest ich szablon.Sprawdź to wszystko tutaj
źródło
type.builder.ts
jak wskazałeś, chciałbym, aby każdy użytkownik zrozumiał, jak to wszystko umieścić w kontekst ... Mam nadzieję, że może się przydać;)EDYCJA (26/08/2017) : Poniższe rozwiązanie działa dobrze z Angular2 i 4. Zaktualizowałem go, aby zawierał zmienną szablonu i moduł obsługi kliknięć, i przetestowałem go z Angular 4.3.
Dla Angular4 ngComponentOutlet, jak opisano w odpowiedzi Ophira, jest znacznie lepszym rozwiązaniem. Ale w tej chwili nie obsługuje jeszcze wejść i wyjść . Jeśli [ten PR] ( https://github.com/angular/angular/pull/15362] zostanie zaakceptowany, byłoby to możliwe poprzez instancję komponentu zwróconą przez zdarzenie create.
Ng-dynamic-component może być najlepszy i najprostszy rozwiązanie w ogóle, ale jeszcze tego nie testowałem.
Odpowiedź @Long Field jest na miejscu! Oto inny (synchroniczny) przykład:
Na żywo pod adresem http://plnkr.co/edit/fdP9Oc .
źródło
ngAfterViewInit
wywołanie zconst template
nie zadziała. Ale jeśli Twoim zadaniem było zredukowanie wyżej opisanego podejścia (utwórz szablon, utwórz komponent, utwórz moduł, skompiluj go, utwórz fabrykę ... utwórz instancję) ... prawdopodobnie to zrobiłeśMusiałem przybyć na imprezę późno, żadne z przedstawionych tu rozwiązań nie wydawało mi się pomocne - zbyt niechlujne i wydawało mi się, że to zbyt duże obejście.
Co skończyło się robi to używając
Angular 4.0.0-beta.6
„s ngComponentOutlet .To dało mi najkrótsze, najprostsze rozwiązanie, wszystkie zapisane w pliku komponentu dynamicznego.
my-component
- komponent, w którym renderowany jest komponent dynamicznyDynamicComponent
- komponent, który ma być budowany dynamicznie i jest renderowany wewnątrz mojego komponentuNie zapomnij zaktualizować wszystkich bibliotek kątowych do ^ Angular 4.0.0
Mam nadzięję, że to pomogło, powodzenia!
AKTUALIZACJA
Działa również dla kątownika 5.
źródło
Odpowiedź na czerwiec 2019 r
Dobre wieści! Wygląda na to, że pakiet @ angular / cdk ma teraz doskonałą obsługę portali !
W chwili pisania tego artykułu nie uważałem powyższych oficjalnych dokumentów za szczególnie pomocne (szczególnie w odniesieniu do wysyłania danych i odbierania zdarzeń z komponentów dynamicznych). Podsumowując, będziesz musiał:
Krok 1) Zaktualizuj swój
AppModule
Zaimportuj
PortalModule
z@angular/cdk/portal
pakietu i zarejestruj w nim komponenty dynamiczneentryComponents
Krok 2. Opcja A: Jeśli NIE musisz przekazywać danych i odbierać zdarzeń z komponentów dynamicznych :
Zobacz to w akcji
Krok 2. Opcja B: Jeśli musisz przekazywać dane i odbierać zdarzenia z komponentów dynamicznych :
Zobacz to w akcji
źródło
Portal
różni się to podejście odngTemplateOutlet
ingComponentOutlet
? 🤔Postanowiłem zebrać wszystko, czego się nauczyłem, w jeden plik . Jest tu wiele do zrobienia, zwłaszcza w porównaniu do RC5. Zauważ, że ten plik źródłowy zawiera AppModule i AppComponent.
źródło
Mam prosty przykład, jak pokazać kątowy komponent dynamiczny 2 rc6.
Załóżmy, że masz dynamiczny szablon HTML = szablon1 i chcesz dynamicznie ładować, najpierw zawinąć w komponent
tutaj szablon1 jako HTML może zawierać komponent ng2
Od rc6, muszę mieć @NgModule opakowujący ten komponent. @NgModule, podobnie jak moduł w anglarJS 1, oddziela różne części aplikacji ng2, więc:
(Tutaj zaimportuj RouterModule, ponieważ w moim przykładzie w html znajduje się kilka składników trasy, co później można zobaczyć)
Teraz możesz skompilować DynamicModule jako:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
I musimy umieścić powyżej w app.moudule.ts, aby go załadować, zobacz mój app.moudle.ts. Aby uzyskać więcej szczegółowych informacji, sprawdź: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts and app.moudle.ts
i zobacz demo: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview
źródło
W wersji kątowej 7.x użyłem do tego elementów kątowych.
Zainstaluj @ angular-elements npm i @ angular / elements -s
Utwórz usługę akcesoriów.
Pamiętaj, że niestandardowy znacznik elementu musi być inny w przypadku selektora komponentu kątowego. w AppUserIconComponent:
w tym przypadku niestandardową nazwę znacznika użyłem „ikona użytkownika”.
lub tak:
(w szablonie):
Zauważ, że w drugim przypadku musisz przekazać obiekty za pomocą JSON.stringify, a następnie ponownie je przeanalizuj. Nie mogę znaleźć lepszego rozwiązania.
źródło
document.createElement(tagName);
Rozwiązano to w wersji Angular 2 Final, wykorzystując po prostu dyrektywę dynamicComponent od ng-dynamic .
Stosowanie:
Gdzie szablon jest Twoim szablonem dynamicznym, a kontekst można ustawić na dowolny dynamiczny model danych, z którym szablon ma się wiązać.
źródło
Chciałbym dodać kilka szczegółów do tego bardzo doskonałego postu autorstwa Radima.
Wziąłem to rozwiązanie i pracowałem nad nim przez chwilę i szybko napotkałem pewne ograniczenia. Po prostu opiszę je, a następnie podam rozwiązanie.
Zadałem kolejne pytanie na podstawie tego postu, w jaki sposób osiągnąć te ograniczenia, które można znaleźć tutaj:
rekurencyjna dynamiczna kompilacja szablonów w angular2
Przedstawię tylko odpowiedzi na te ograniczenia, jeśli napotkasz ten sam problem, co ja, ponieważ dzięki temu rozwiązanie będzie bardziej elastyczne. Byłoby wspaniale, gdyby zaktualizowano również początkowy plunker.
Aby włączyć zagnieżdżanie szczegółów dynamicznych między sobą, musisz dodać DynamicModule.forRoot () w instrukcji importu w type.builder.ts
Poza tym nie było możliwe użycie
<dynamic-detail>
w jednej z części edytora tekstu lub edytora tekstu.Aby to włączyć, musisz zmienić
parts.module.ts
idynamic.module.ts
Wewnątrz
parts.module.ts
Musisz dodaćDynamicDetail
wDYNAMIC_DIRECTIVES
Również w
dynamic.module.ts
trzeba będzie usunąć dynamicDetail, ponieważ są one teraz częścią częściDziałający zmodyfikowany plunker można znaleźć tutaj: http://plnkr.co/edit/UYnQHF?p=preview (nie rozwiązałem tego problemu, jestem tylko posłańcem :-D)
Wreszcie nie było możliwe użycie szablonów w częściach utworzonych na komponentach dynamicznych. Rozwiązaniem (lub obejściem problemu. Nie jestem pewien, czy jest to błąd kątowy, czy nieprawidłowe użycie frameworka) było utworzenie kompilatora w konstruktorze zamiast wstrzykiwania go.
Następnie użyj
_compiler
do kompilacji, a następnie szablonUrls są również włączone.Mam nadzieję, że to pomoże komuś innemu!
Z pozdrowieniami Morten
źródło
W następstwie doskonałej odpowiedzi Radmin, potrzebna jest drobna poprawka dla każdego, kto używa wersji kątowej cli w wersji 1.0.0-beta.22 i wyższej.
COMPILER_PROVIDERS
nie można już importować (szczegółowe informacje można znaleźć w GitHub angular-cli ).Zatem obejściem, którego w ogóle nie należy używać
COMPILER_PROVIDERS
iwJitCompiler
tejproviders
sekcji, ale należy użyćJitCompilerFactory
„@ angular / compiler” zamiast tego w klasie konstruktora typów:Jak widać, nie można go wstrzykiwać, a zatem nie ma zależności od DI. To rozwiązanie powinno także działać w projektach, które nie używają angular-cli.
źródło
Sam próbuję zobaczyć, jak mogę zaktualizować RC4 do RC5, dlatego natknąłem się na ten wpis, a nowe podejście do dynamicznego tworzenia komponentów wciąż jest dla mnie nieco tajemnicą, więc nie sugeruję niczego na temat resolvera fabryki komponentów.
Ale mogę zasugerować nieco jaśniejsze podejście do tworzenia komponentów w tym scenariuszu - wystarczy użyć przełącznika w szablonie, który utworzyłby edytor ciągów lub edytor tekstu zgodnie z pewnymi warunkami, takimi jak ten:
A tak przy okazji, „[” w wyrażeniu [prop] ma znaczenie, oznacza to jednokierunkowe wiązanie danych, dlatego możesz, a nawet powinieneś je pominąć, jeśli wiesz, że nie musisz wiązać właściwości ze zmienną.
źródło
switch
/case
zawiera kilka decyzji. Ale wyobraź sobie, że wygenerowany szablon może być naprawdę duży ... i różnić się dla każdej jednostki, różnić się pod względem bezpieczeństwa, różnią się statusem jednostki, według każdego typu właściwości (liczba, data, odniesienie ... redaktorzy) ... W takim przypadku rozwiązanie tego w szablonie HTMLngSwitch
utworzyłoby duży, bardzo dużyhtml
plik.To jest przykład dynamicznych kontrolek Form generowanych z serwera.
https://stackblitz.com/edit/angular-t3mmg6
Ten przykład przedstawia dynamiczne formanty Form w dodawanym składniku (w tym miejscu można uzyskać Formcontrols z serwera). Jeśli zobaczysz metodę addcomponent, możesz zobaczyć Forms Controls. W tym przykładzie nie używam materiału kątowego, ale działa (używam @ work). Jest to celowane do kątowego 6, ale działa we wszystkich poprzednich wersjach.
Musisz dodać JITComplierFactory dla AngularVersion 5 i nowszych.
Dzięki
Vijay
źródło
W tym konkretnym przypadku lepszym rozwiązaniem byłoby użycie dyrektywy do dynamicznego tworzenia komponentu. Przykład:
W kodzie HTML, w którym chcesz utworzyć komponent
Podejdę i zaprojektuję dyrektywę w następujący sposób.
Tak więc w twoich komponentach będzie dostępny tekst, ciąg, data, cokolwiek - bez względu na konfigurację, którą przekazałeś w HTML w
ng-container
elemencie.Konfiguracja
yourConfig
może być taka sama i definiować metadane.W zależności od konfiguracji lub typu danych wejściowych dyrektywa powinna działać odpowiednio, a na podstawie obsługiwanych typów będzie renderować odpowiedni komponent. Jeśli nie, zarejestruje błąd.
źródło
Opierając się na odpowiedzi Ophira Sterna, oto wariant, który działa z AoT w Angular 4. Jedynym problemem, jaki mam, jest to, że nie mogę wstrzyknąć żadnych usług do komponentu DynamicComponent, ale mogę z tym żyć.
Uwaga: Nie testowałem z Angular 5.
Mam nadzieję że to pomoże.
Twoje zdrowie!
źródło