Je veux créer dynamiquement un modèle. Cela devrait être utilisé pour construire un ComponentType
au moment de l'exécution et le placer (même le remplacer) quelque part à l'intérieur du composant d'hébergement.
Jusqu'au RC4 que j'utilisais ComponentResolver
, mais avec RC5 j'obtiens le message suivant:
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.
J'ai trouvé ce document ( Création de composant dynamique synchrone angulaire 2 )
Et je comprends que je peux utiliser soit
- Type de dynamique
ngIf
avecComponentFactoryResolver
. Si je passe des composants connus à l'intérieur de@Component({entryComponents: [comp1, comp2], ...})
- je peux utiliser.resolveComponentFactory(componentToRender);
- Véritable compilation d'exécution, avec
Compiler
...
Mais la question est de savoir comment utiliser cela Compiler
? La note ci-dessus dit que je devrais appeler: Compiler.compileComponentSync/Async
- alors comment?
Par exemple. Je veux créer (en fonction de certaines conditions de configuration) ce type de modèle pour un type de paramètres
<form>
<string-editor
[propertyName]="'code'"
[entity]="entity"
></string-editor>
<string-editor
[propertyName]="'description'"
[entity]="entity"
></string-editor>
...
et dans un autre cas celui-ci ( string-editor
est remplacé par text-editor
)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
Et ainsi de suite (nombre / date / référence différent editors
selon les types de propriétés, ignoré certaines propriétés pour certains utilisateurs ...) . c'est-à-dire qu'il s'agit d'un exemple, la configuration réelle pourrait générer des modèles beaucoup plus différents et complexes.
Le modèle est en train de changer, donc je ne peux pas utiliser ComponentFactoryResolver
et transmettre ceux existants ... J'ai besoin d'une solution avec le Compiler
.
la source
$compile
pourrait réellement faire que ces méthodes ne peuvent pas - je crée une application où je veux simplement compiler le code HTML tel qu'il arrive via une page tierce et des appels ajax. Je ne peux pas supprimer le code HTML de la page et le placer dans mon propre modèle. SoupirRéponses:
EDIT - lié à 2.3.0 (2016-12-07)
Un sujet similaire est discuté ici Equivalent de $ compile dans Angular 2 . Nous devons utiliser
JitCompiler
etNgModule
. En savoir plus surNgModule
Angular2 ici:En un mot
Il existe un plunker / exemple fonctionnel (modèle dynamique, type de composant dynamique, module dynamique ,, ... en action)
JitCompiler
Le principe est:
1) créer un modèle
2) trouver
ComponentFactory
dans le cache - aller à 7)3) - créer
Component
4) - créer
Module
5) - compiler
Module
6) - retourner (et mettre en cache pour une utilisation ultérieure)
ComponentFactory
7) utiliser Target et
ComponentFactory
créer une instance de dynamiqueComponent
Voici un extrait de code (plus ici ) - Notre générateur personnalisé retourne juste construit / mis en cache
ComponentFactory
et l'espace réservé de vue cible consomme pour créer une instance de laDynamicComponent
Ça y est - en bref. Pour obtenir plus de détails .. lire ci-dessous
.
TL&DR
Observez un plongeur et revenez pour lire les détails au cas où un extrait nécessiterait plus d'explications
.
Explication détaillée - Angular2 RC6 ++ et composants d'exécution
Ci-dessous la description de ce scénario , nous allons
PartsModule:NgModule
(support de petites pièces)DynamicModule:NgModule
, qui contiendra notre composant dynamique (et référenceraPartsModule
dynamiquement)Component
type (uniquement si le modèle a changé)RuntimeModule:NgModule
. Ce module contiendra leComponent
type précédemment crééJitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
pour obtenirComponentFactory
DynamicComponent
tâche - de l'espace réservé View Target etComponentFactory
@Inputs
à nouvelle instance (commutateur deINPUT
deTEXTAREA
montage) , à consommer@Outputs
NgModule
Nous avons besoin d'un
NgModule
s.Il y aura un module pour tous les petits composants, par exemple
string-editor
,text-editor
(date-editor
,number-editor
...)Le second sera un module pour notre gestion dynamique des choses. Il contiendra des composants d'hébergement et certains fournisseurs .. qui seront des singletons. Nous les publierons donc de manière standard - avec
forRoot()
Enfin, nous aurons besoin d'un module d'exécution adhoc .. mais qui sera créé plus tard, dans le cadre du
DynamicTypeBuilder
travail.Le quatrième module, module d'application, est celui qui continue de déclarer les fournisseurs de compilateur:
Lisez (lisez) beaucoup plus sur NgModule ici:
Un constructeur de modèles
Dans notre exemple, nous traiterons les détails de ce type d' entité
Pour créer un
template
, dans ce plunker, nous utilisons ce constructeur simple / naïf.Une astuce ici est - il construit un modèle qui utilise un ensemble de propriétés connues, par exemple
entity
. Ces propriétés doivent faire partie d'un composant dynamique, que nous créerons ensuite.Pour le rendre un peu plus facile, nous pouvons utiliser une interface pour définir les propriétés, que notre constructeur de modèles peut utiliser. Ceci sera implémenté par notre type de composant dynamique.
Un
ComponentFactory
constructeurLa chose très importante ici est de garder à l'esprit:
Nous touchons donc au cœur de notre solution. Le Builder, 1) créera
ComponentType
2) créera saNgModule
3) compileraComponentFactory
4) le mettra en cache pour une réutilisation ultérieure.Une dépendance que nous devons recevoir:
Et voici un extrait comment obtenir un
ComponentFactory
:Et voici deux méthodes, qui représentent la manière vraiment cool de créer des classes / types décorés en runtime. Non seulement
@Component
mais aussi@NgModule
Important:
ComponentFactory
utilisé par le composant d'hébergementLa pièce finale est un composant, qui héberge la cible de notre composant dynamique, par exemple
<div #dynamicContentPlaceHolder></div>
. Nous obtenons une référence et utilisonsComponentFactory
pour créer un composant. C'est en un mot, et voici toutes les pièces de ce composant (si nécessaire, ouvrez le plongeur ici )Résumons d'abord les instructions d'importation:
Nous venons de recevoir, les créateurs de modèles et de composants. Viennent ensuite les propriétés qui sont nécessaires pour notre exemple (plus dans les commentaires)
Dans ce scénario simple, notre composant d'hébergement n'en a pas
@Input
. Il n'a donc pas à réagir aux changements. Mais malgré cela (et pour être prêt pour les changements à venir) - nous devons introduire un indicateur si le composant a déjà (tout d'abord) été lancé. Et alors seulement, nous pouvons commencer la magie.Enfin, nous utiliserons notre constructeur de composants, et il vient d'être compilé / mis en cache
ComponentFacotry
. Notre espace réservé cible sera invité à instancier leComponent
avec cette usine.petite extension
De plus, nous devons conserver une référence au modèle compilé .. pour pouvoir le correctement
destroy()
, chaque fois que nous le changerons.terminé
C'est à peu près tout. N'oubliez pas de détruire tout ce qui a été construit dynamiquement (ngOnDestroy) . Assurez-vous également de mettre en cache dynamique
types
etmodules
si la seule différence est leur modèle.Vérifiez tout en action ici
la source
type.builder.ts
comme vous l' avez dit, je souhaite, que tout utilisateur comprendrait comment placer que tout en contexte ... J'espère que cela pourrait être utile;)EDIT (26/08/2017) : La solution ci-dessous fonctionne bien avec Angular2 et 4. Je l'ai mise à jour pour contenir une variable de modèle et un gestionnaire de clics et l'ai testée avec Angular 4.3.
Pour Angular4, ngComponentOutlet comme décrit dans la réponse d'Ophir est une bien meilleure solution. Mais pour le moment, il ne prend pas encore en charge les entrées et sorties . Si [ce PR] ( https://github.com/angular/angular/pull/15362] est accepté, il serait possible via l'instance de composant renvoyée par l'événement create.
Ng-dynamic-component peut être le meilleur et le plus simple solution, mais je ne l'ai pas encore testée.
La réponse de @Long Field est parfaite! Voici un autre exemple (synchrone):
En direct sur http://plnkr.co/edit/fdP9Oc .
la source
ngAfterViewInit
appel avec unconst template
ne fonctionnera pas. Mais si votre tâche était de réduire l'approche décrite ci-dessus détaillée (créer un modèle, créer un composant, créer un module, le compiler, créer une fabrique .. créer une instance) ... vous l'avez probablement faitJe dois être arrivé à la fête tard, aucune des solutions ici ne m'a semblé utile - trop compliquée et me semblait être une solution de contournement.
Ce que je fini par faire est à l' aide
Angular 4.0.0-beta.6
de » ngComponentOutlet .Cela m'a donné la solution la plus courte et la plus simple écrite dans le fichier du composant dynamique.
my-component
- le composant dans lequel un composant dynamique est renduDynamicComponent
- le composant à construire dynamiquement et il est rendu à l'intérieur de mon composantN'oubliez pas de mettre à niveau toutes les bibliothèques angulaires vers ^ Angular 4.0.0
En espérant que ça aide, bonne chance!
METTRE À JOUR
Fonctionne également pour angulaire 5.
la source
Réponse de juin 2019
Bonne nouvelle! Il semble que le paquet @ angular / cdk dispose désormais d'un support de première classe pour les portails !
Au moment de la rédaction, je n'ai pas trouvé les documents officiels ci-dessus particulièrement utiles (en particulier en ce qui concerne l'envoi de données dans et la réception d'événements des composants dynamiques). En résumé, vous devrez:
Étape 1) Mettez à jour votre
AppModule
Importez à
PortalModule
partir du@angular/cdk/portal
package et enregistrez vos composants dynamiques à l'intérieurentryComponents
Étape 2. Option A: si vous n'avez PAS besoin de transmettre des données et de recevoir des événements de vos composants dynamiques :
Voyez-le en action
Étape 2. Option B: Si vous devez transmettre des données et recevoir des événements de vos composants dynamiques :
Voyez-le en action
la source
Portal
approche diffère-t-elle dengTemplateOutlet
etngComponentOutlet
? 🤔J'ai décidé de compacter tout ce que j'ai appris dans un seul fichier . Il y a beaucoup à retenir ici, surtout par rapport à avant RC5. Notez que ce fichier source inclut AppModule et AppComponent.
la source
J'ai un exemple simple pour montrer comment faire un composant dynamique angulaire 2 rc6.
Dites, vous avez un template html dynamique = template1 et souhaitez charger dynamiquement, envelopper d'abord
ici template1 en html, peut contenir un composant ng2
Depuis rc6, @NgModule doit envelopper ce composant. @NgModule, tout comme le module dans anglarJS 1, il dissocie différentes parties de l'application ng2, donc:
(Ici, importez RouterModule comme dans mon exemple, il y a des composants de route dans mon html comme vous pouvez le voir plus tard)
Vous pouvez maintenant compiler DynamicModule comme:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
Et nous devons mettre ci-dessus dans app.moudule.ts pour le charger, s'il vous plaît voir mon app.moudle.ts. Pour plus de détails, consultez: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts et app.moudle.ts
et voir la démo: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview
la source
Dans la version 7.x angulaire, j'ai utilisé des éléments angulaires pour cela.
Installer @ angular-elements npm i @ angular / elements -s
Créez un service accessoire.
Notez que votre balise d'élément personnalisée doit être différente avec le sélecteur de composant angulaire. dans AppUserIconComponent:
et dans ce cas, le nom de la balise personnalisée, j'ai utilisé "user-icon".
ou comme ça:
(dans le modèle):
Notez que dans le deuxième cas, vous devez passer des objets avec JSON.stringify et ensuite les analyser à nouveau. Je ne peux pas trouver de meilleure solution.
la source
document.createElement(tagName);
Résolution de ce problème dans la version finale d'Angular 2 simplement en utilisant la directive dynamicComponent de ng-dynamic .
Usage:
Où modèle est votre modèle dynamique et le contexte peut être défini sur n'importe quel modèle de données dynamique auquel vous souhaitez que votre modèle se lie.
la source
Je veux ajouter quelques détails en plus de cet excellent article de Radim.
J'ai pris cette solution et y ai travaillé un peu et j'ai rapidement rencontré certaines limites. Je vais simplement les décrire, puis donner la solution à cela également.
J'ai fait une autre question basée sur ce post, sur la façon d'atteindre ces limitations, qui peut être trouvée ici:
compilation récursive de modèles dynamiques dans angular2
Je vais simplement décrire les réponses à ces limitations, si vous rencontrez le même problème que moi, car cela rend la solution beaucoup plus flexible. Ce serait génial d'avoir également mis à jour le plongeur initial avec cela.
Pour activer l'imbrication des détails dynamiques les uns dans les autres, vous devrez ajouter DynamicModule.forRoot () dans l'instruction d'importation dans le type.builder.ts
En outre, il n'était pas possible d'utiliser
<dynamic-detail>
à l'intérieur de l'une des parties étant l'éditeur de chaîne ou l'éditeur de texte.Pour activer cela, vous devrez changer
parts.module.ts
etdynamic.module.ts
À l'intérieur,
parts.module.ts
vous devrez ajouterDynamicDetail
leDYNAMIC_DIRECTIVES
Aussi dans le,
dynamic.module.ts
vous devrez supprimer le dynamicDetail car ils font maintenant partie des partiesUn plunker modifié qui fonctionne peut être trouvé ici: http://plnkr.co/edit/UYnQHF?p=preview (je n'ai pas résolu ce problème, je suis juste le messager :-D)
Enfin il n'a pas été possible d'utiliser des templateurs dans les pièces créées sur les composants dynamiques. Une solution (ou solution de contournement. Je ne sais pas si c'est un bogue angulaire ou une mauvaise utilisation du framework) était de créer un compilateur dans le constructeur au lieu de l'injecter.
Utilisez ensuite le
_compiler
pour compiler, les templateUrls sont également activés.J'espère que ceci aide quelqu'un d'autre!
Cordialement Morten
la source
Pour donner suite à l'excellente réponse de Radmin, un petit ajustement est nécessaire pour tous ceux qui utilisent angular-cli version 1.0.0-beta.22 et plus.
COMPILER_PROVIDERS
ne peut plus être importé (pour plus de détails, voir angular-cli GitHub ).Ainsi, la solution de contournement consiste à ne pas utiliser du tout
COMPILER_PROVIDERS
etJitCompiler
dans laproviders
section, mais à utiliser à la placeJitCompilerFactory
de '@ angular / compiler' comme ceci à l'intérieur de la classe de générateur de type:Comme vous pouvez le voir, il n'est pas injectable et n'a donc pas de dépendances avec la DI. Cette solution devrait également fonctionner pour les projets n'utilisant pas angular-cli.
la source
J'essaie moi-même de voir comment puis-je mettre à jour RC4 vers RC5 et donc je suis tombé sur cette entrée et la nouvelle approche de la création de composants dynamiques me tient toujours un peu de mystère, donc je ne suggérerai rien sur le résolveur d'usine de composants.
Mais, ce que je peux suggérer est une approche un peu plus claire de la création de composants dans ce scénario - utilisez simplement un commutateur dans le modèle qui créerait un éditeur de chaîne ou un éditeur de texte selon certaines conditions, comme ceci:
Et en passant, "[" dans l'expression [prop] a un sens, cela indique une liaison de données à sens unique, donc vous pouvez et même devez les omettre si vous savez que vous n'avez pas besoin de lier la propriété à la variable.
la source
switch
/case
contient peu de décisions. Mais imaginez que le modèle généré puisse être très volumineux ... et différer pour chaque entité, différer par la sécurité, différer par le statut de l'entité, par chaque type de propriété (numéro, date, référence ... éditeurs) ... Dans ce cas, résoudre ce problème dans un modèle htmlngSwitch
créerait unhtml
fichier volumineux, très très volumineux .C'est l'exemple des contrôles de formulaire dynamiques générés à partir du serveur.
https://stackblitz.com/edit/angular-t3mmg6
Cet exemple montre que les contrôles de formulaire dynamiques sont dans le composant add (c'est ici que vous pouvez obtenir les contrôles de formulaire à partir du serveur). Si vous voyez la méthode addcomponent, vous pouvez voir les contrôles de formulaires. Dans cet exemple, je n'utilise pas de matériau angulaire, mais cela fonctionne (j'utilise @ work). Ceci est destiné à angular 6, mais fonctionne dans toutes les versions précédentes.
Besoin d'ajouter JITComplierFactory pour AngularVersion 5 et supérieur.
Merci
Vijay
la source
Pour ce cas particulier, l'utilisation d'une directive pour créer dynamiquement le composant serait une meilleure option. Exemple:
Dans le HTML où vous souhaitez créer le composant
J'aborderais et concevrais la directive de la manière suivante.
Ainsi, dans vos composants, texte, chaîne, date, peu importe - quelle que soit la configuration que vous avez passée dans le code HTML de l'
ng-container
élément serait disponible.La configuration,,
yourConfig
peut être la même et définir vos métadonnées.Selon votre configuration ou type d'entrée, la directive devrait agir en conséquence et à partir des types pris en charge, elle rendrait le composant approprié. Sinon, il enregistrera une erreur.
la source
S'appuyant sur la réponse d'Ophir Stern, voici une variante qui fonctionne avec AoT dans Angular 4. Le seul problème que j'ai est que je ne peux pas injecter de services dans DynamicComponent, mais je peux vivre avec ça.
remarque: je n'ai pas testé avec Angular 5.
J'espère que cela t'aides.
À votre santé!
la source