Implémentation d’un framework de plugin architecture / plugin system / pluggable dans Angular 2, 4, 5, 6

Mise à jour 24/05/2018: Nous sums maintenant des versions +3 d’Angular depuis mon message initial et nous n’avons toujours pas de solution définitive. Lars Meijdam (@LarsMeijdam) a mis au point une approche intéressante qui vaut certainement le détour. (En raison de problèmes de propriété, il a dû supprimer temporairement le référentiel GitHub où il avait initialement posté son échantillon. Cependant, vous pouvez lui envoyer un message directement si vous souhaitez en obtenir une copie. Veuillez consulter les commentaires ci-dessous pour plus d’informations.)

Les modifications architecturales récentes apscopes à Angular 6 nous rapprochent d’une solution. De plus, les éléments angulars ( https://angular.io/guide/elements ) fournissent des fonctionnalités de composant – mais pas tout à fait ce que j’ai décrit à l’origine dans cet article.

Si quelqu’un de l’incroyable équipe Angular arrive à trouver cela, veuillez noter qu’il semble y avoir beaucoup d’autres personnes qui sont également très intéressées par cette fonctionnalité. Il pourrait bien être intéressant de considérer l’arriéré.


Je voudrais implémenter un framework enfichable (plug-in) dans une application Angular 2 , Angular 4 , Angular 5 ou Angular 6 .

(Mon cas particulier de développement de ce framework enfichable est que je dois développer un système de gestion de contenu miniature. Pour un certain nombre de raisons qui ne sont pas nécessairement développées ici, Angular 2/4/5/6 convient parfaitement à la plupart des besoins. de ce système.)

Par framework enfichable (ou architecture de plug-in), j’entends spécifiquement un système qui permet aux développeurs tiers de créer ou d’étendre les fonctionnalités d’une application principale via l’utilisation de composants enfichables sans avoir directement access au code source de l’application principale. ou fonctionnement interne .

(Cette formulation sur ” sans avoir directement access au code source de l’application ou à son fonctionnement ” est un objective essentiel).

Des exemples de frameworks enfichables incluent des systèmes de gestion de contenu communs tels que WordPress ou Drupal .

La situation idéale (comme avec Drupal) serait de pouvoir placer ces composants enfichables (ou plug-ins) dans un dossier, de faire en sorte que l’application les détecte automatiquement ou les découvre, et les fait “fonctionner” comme par magie. Cela se produirait d’une manière hot-plug, c’est-à-dire que l’application était en cours d’exécution, serait optimale.

J’essaie actuellement de déterminer ( avec votre aide ) les réponses aux cinq questions suivantes.

  1. Praticité: Un framework de plugin pour une application Angular 2/4/5/6 même pratique? (Jusqu’à présent, je n’ai trouvé aucun moyen pratique de créer un cadre réellement connectable avec Angular2/4/5/6 .)
  2. Défis attendus: Quels défis pourrait-on rencontrer en implémentant un framework de plugin pour une application Angular 2/4/5/6 ?
  3. Stratégies de mise en œuvre: Quelles techniques ou stratégies spécifiques pourraient être utilisées pour implémenter un framework de plug-in pour une application Angular 2/4/5/6 ?
  4. Meilleures pratiques: Quelles sont les meilleures pratiques pour implémenter un système de plug-in pour une application Angular 2/4/5/6 ?
  5. Technologies alternatives: Si un framework de plugin n’est pas pratique dans une application Angular 2/4/5/6 , quelles technologies relativement équivalentes (par exemple, React ) pourraient convenir à une application Web hautement réactive moderne ?

En général, l’utilisation de l’ Angular 2/4/5/6 est très souhaitable car:

  • il est naturellement extrêmement rapide – de manière éclatante.
  • il consum très peu de bande passante (après le chargement initial)
  • son encombrement est relativement faible (après le tree shaking et tree shaking ) – et cette empreinte continue de diminuer
  • il est très fonctionnel et l’équipe Angular et la communauté continuent de croître rapidement de son écosystème
  • il joue bien avec la plupart des technologies Web les meilleures et les plus récentes telles que TypeScript et Observables
  • Angular 5 prend désormais en charge les travailleurs des services ( https://medium.com/@webmaxru/a-new-angular-service-worker-creating-automatic-progressive-web-apps-part-1-theory-37d7d7647cc7 )
  • étant soutenu par Google , il sera probablement pris en charge et amélioré dans le futur

J’aimerais beaucoup utiliser Angular 2/4/5/6 pour mon projet actuel. Si je peux utiliser Angular 2/4/5/6 , j’utiliserai également Angular-CLI et probablement Angular Universal (pour le rendu côté serveur).

Voici mes pensées, jusqu’ici, concernant les questions ci-dessus. S’il vous plaît examiner et fournir vos commentaires et éclaircissements.

  • Angular 2/4/5/6 applications Angular 2/4/5/6 consumnt des packages – mais ce n’est pas nécessairement la même chose que d’autoriser des plug-ins au sein d’une application. Un plugin dans d’autres systèmes (par exemple Drupal ) peut être ajouté en déposant le dossier du plugin dans un répertoire commun des modules où il est automatiquement “capté” par le système. Dans Angular 2/4/5/6 , un package (en tant que plug-in) est généralement installé via npm , ajouté à package.json , puis importé manuellement dans l’application – comme dans app.module . Ceci est beaucoup plus compliqué que la méthode Drupal pour supprimer un dossier et pour que le système détecte automatiquement le paquet. Plus il est compliqué d’installer un plugin, moins les gens seront susceptibles de les utiliser. Ce serait bien mieux si Angular 2/4/5/6 détecter et installer automatiquement les plugins. Je suis très intéressé de trouver une méthode qui permet aux non-développeurs d’installer l’application Angular 2/4/5/6 et d’installer les plugins choisis sans avoir à comprendre toute l’architecture de l’application.

  • En général, l’un des avantages de fournir une architecture enfichable est qu’il est très facile pour les développeurs tiers d’étendre les fonctionnalités du système. De toute évidence, ces développeurs ne seront pas familiarisés avec toutes les subtilités du code pour l’application à laquelle ils se connectent. Une fois les plugins développés, d’autres utilisateurs encore moins techniques peuvent simplement installer l’application et tous les plugins sélectionnés. Cependant, Angular 2/4/5/6 est relativement compliqué et a une courbe d’apprentissage très longue. Pour compliquer encore les choses, la plupart des applications Angular 2/4/5/6 production utilisent également des Angular-CLI , Angular Universal et WebPack . Quelqu’un qui met en œuvre un plug-in devrait probablement avoir au moins quelques connaissances de base sur la manière dont tous ces éléments s’emboîtent – ainsi qu’une solide connaissance pratique de TypeScript et une connaissance raisonnable de NodeJS . Les besoins en connaissances sont-ils si extrêmes qu’aucun tiers ne voudrait jamais développer un plug-in?

  • La plupart des plugins auront probablement un composant côté serveur (par exemple, pour stocker / récupérer des données relatives aux plug-ins) ainsi que des sorties côté client. Angular 2/4/5 particulier (et fortement), décourage les développeurs d’injecter leurs propres modèles lors de l’exécution, car cela pose un grave problème de sécurité. Afin de gérer de nombreux types de sortie qu’un plugin peut accepter (par exemple l’affichage d’un graphique), il semble que permettre aux utilisateurs de créer du contenu qui est injecté dans le stream de réponse, sous une forme ou une autre, est probablement nécessaire. Je me demande comment il serait possible de répondre à ce besoin sans déchiqueter figurativement les mécanismes de sécurité Angular 2/4/5/6 .

  • La plupart des applications Angular 2/4/5/6 production sont pré-compilées en utilisant la compilation Ahead of Time ( AOT ). (Tout devrait probablement l’être.) Je ne sais pas comment les plug-ins peuvent être ajoutés (ou intégrés) aux applications pré-compilées. Le meilleur scénario impliquerait la compilation des plug-ins séparément de l’application principale. Cependant, je ne sais pas comment faire ce travail. Une solution de rechange pourrait consister à recomstackr l’application entière avec tous les plugins inclus, mais cela compliquerait un peu les choses pour un administrateur qui veut simplement installer l’application (sur son propre serveur) avec tous les plugins sélectionnés.

  • Dans une application Angular 2/4/5/6 , en particulier une application pré-compilée, un seul code erratique ou conflictuel peut casser l’application entière. Angular 2/4/5/6 applications Angular 2/4/5/6 ne sont pas toujours les plus faciles à déboguer. L’application de plugins mal conduits pourrait entraîner des expériences très désagréables. Je ne suis actuellement pas au courant d’un mécanisme pour gérer avec élégance les plugins mal conduits.

Je viens de publier un nouveau chapitre pour mon livre ” Developing with Angular ” qui aborde le sujet des plug-ins dans Angular 2+ et devrait intéresser les personnes qui tentent de construire des plug-ins externes.

Points clés:

  • Plugins
  • Composants de construction basés sur des noms de chaîne
  • Chargement de la configuration à partir de sources externes
  • Modification dynamic des itinéraires d’application
  • Plugins externes
  • Création de bibliothèques de plugins
  • Chargement de plugins dans l’application
  • Routes dynamics avec contenu de plugin

Le livre est gratuit et a un modèle “payez ce que vous voulez”. N’hésitez pas à prendre une copie et à espérer que cela aide.

J’ai créé un repository sur github avec une solution qui pourrait aider. Il utilise des bibliothèques Angular 6 et 1 applications de base qui chargent les bibliothèques groupées UMD paresseusement; https://github.com/lmeijdam/angular-umd-dynamic-example

Si vous avez des suggestions, n’hésitez pas à append!

Exemple d’application avec un système de plugin fonctionnel (grâce à Gijs pour la création du repo github!) https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-10 basé sur le eBook Mastering Angular 2 Components

  • architecture de plugin pour étendre les composants principaux de l’application
  • système de plug-in de fichiers (pour append simplement des répertoires / fichiers de plug-in sans éditer les fichiers de configuration de base ou la nécessité de recomstackr votre application!)
  • charger et utiliser dynamicment les plugins
  • construire un gestionnaire de plug-ins rudimentaire pour activer / désactiver les plug-ins à la volée

Bravo, Niklas

Ce que vous recherchez, c’est un chargement par module paresseux. En voici un exemple: http://plnkr.co/edit/FDaiDvklexT68BTaNqvE?p=preview

 import {Component} from '@angular/core'; import {Router} from '@angular/router'; @Component({ selector: 'my-app', template: ` Home | App Home | App Lazy 

` }) export class App { loaded: boolean = false; constructor(private router: Router) {} addRoutes() { let routerConfig = this.router.config; if (!this.loaded) { routerConfig[1].children.push({ path: `lazy`, loadChildren: 'app/lazy.module#LazyModule' }); this.router.resetConfig(routerConfig); this.loaded = true; } } }

Meilleur … Tom

J’ai fait un hack pour charger et comstackr d’autres modules dans le temps de bootstrap, mais je n’ai pas résolu le problème des dépendances cycliques

  const moduleFile: any = require(`./${app}/${app}.module`), module = moduleFile[Object.keys(moduleFile)[0]]; route.children.push({ path: app, loadChildren: (): Promise => module }); promises.push(this.comstackr.comstackModuleAndAllComponentsAsync(module)); 

alors dans AppModule ajoutez ceci:

 { provide: APP_INITIALIZER, useFactory: AppsLoaderFactory, deps: [AppsLoader], multi: true }, 

Je cherchais également un système de plugin en angular 2/4 pour développer un environnement RAD pour une application d’entreprise au travail. Après quelques recherches, j’ai décidé d’implémenter un ensemble de composants pseudo-angulars stockés dans la firebase database (mais pouvant se trouver dans le système de fichiers).

Les composants stockés dans la firebase database sont basés sur ng-dynamic et l’implémentation du composant principal est similaire à ceci:

 declare var ctx: any; @Component({ selector: 'my-template', template: ` 
`, providers: [EmitterService], }) export class MyTemplateComponent implements OnMount, AfterViewInit, OnChanges { // name private _name: ssortingng; get name(): ssortingng { return this._name; } @Input() set name(name: ssortingng) { this._name = name; this.initTemplate(); } template: ssortingng; ctx: any = null; private initTemplate() { this.templateSvc.getTemplate(this.name).subscribe(res => { // Load external JS with ctx implementation let promise1 = injectScript(res.pathJs); // Load external CCS let promise2 = injectScript(res.pathCss); Promise.all([promise1, promise2]).then(() => { // assign external component code this.ctx = ctx; // // sets the template this.template = res.template; this.injectServices(); if (this.ctx && this.ctx.onInit) { this.ctx.onInit(); } }); }); }

Le code javascript externe est similaire aux composants angulars:

 var ctx = { // injected _httpService: {}, _emitterService: null, // properies model: { "title": "hello world!", }, // events onInit() { console.log('onInit'); }, onDestroy() { console.log('onDestroy'); }, onChanges(changes) { console.log('changes', changes); }, customFunction1() { console.log('customFunction1'); }, childTemplateName: ssortingng = 'other-component'; }; 

Et les modèles des composants sont comme des modèles angulars:

 {{ ctx.model.title }}  

Et pourrait être nested aussi:

 {{ ctx.model.title }}  

Bien que ce ne soit pas parfait, les développeurs des composants personnalisés ont un cadre similaire à celui d’angular2 / 4.

Cela peut être fait “manuellement”. Webpack ne connaissant rien du module externe (plug-ins), il ne peut pas les inclure dans un ou plusieurs bundles. Donc ce que j’ai fait, c’est de regarder le code généré par webpack et j’ai trouvé ces tartes de code dans main.bundle.js:

 var map = { "./dashboard/dashboard.module": ["../../../../../src/app/dashboard/dashboard.module.ts","dashboard.module"]}; 

Permet d’examiner ce que contient ce tableau:

  1. “./dashboard/dashboard.module” – ceci est l’URL de routage du module que nous voulons charger par exemple: {path: ‘dashboard’, loadChildren: ‘./dashboard/dashboard.module#DashboardModule’}
  2. “../../../../../src/app/dashboard/dashboard.module.ts” – ceci est le point d’entrée (constructeur) de
  3. “dashboard.module” – nom de fichier réel sans chunk.js (par exemple: dashboard.module.chunk.js )

Donc, en théorie, si vous ajoutez une entrée à la propriété map, configurez votre routage et suivez le modèle, vous pouvez avoir un système de plug-in. Le défi consiste maintenant à append ou à supprimer des entrées de cette propriété de carte. Évidemment, cela ne peut pas être fait à partir d’un code angular, cela devrait être fait pour un outil externe.

J’ai essayé d’implémenter une architecture de plugin utilisant ABP, Angular et ASP.NET Core: https://github.com/chanjunweimy/abp_plugin_with_ui

Fondamentalement, j’ai développé des plugins angulars en utilisant différentes applications angulars, puis je les ajoute dynamicment ensemble.

Plus d’informations sur comment je l’obtiens:

J’ai 2 applications angulars-cli, 1 est l’application angular cli principale et une autre est l’application cli angular de plugin. Le problème auquel nous sums confrontés dans l’approche de l’architecture de plug-in Angular-cli est la façon dont nous les intégrons.

En ce moment, j’ai exécuté ng-build sur les deux applications et les ai placées dans un dossier “wwwroot”, qui était ensuite hébergé dans un serveur ASP.NET core 2.0. Un référentiel plus simple qui montre cette idée est Angular Multiple App: https://github.com/chanjunweimy/angular-multiple-app

abp_plugin_with_ui est un repository qui travaille sur le développement d’un plugin contenant à la fois le backend et le cli angular. Pour le backend, j’ai utilisé le framework aspnetboilerplate, que le frontend est développé en utilisant plusieurs applications angulars-cli.

Pour que l’application principale soit intégrée à l’application du plugin, il faut lancer “ng-build” sur les deux applications (notez que nous devons également changer le href de l’application du plugin), puis nous déplaçons le contenu construit du plugin application cli angular, dans le dossier “wwwroot” de l’application principale. Après avoir réalisé tout cela, nous pouvons alors exécuter “dotnet run” pour servir l’application Web ASP.NET Core 2.0 pour héberger les fichiers statiques générés par “ng build”.

J’espère que ça aide. Tout commentaire est le bienvenu! ^^

Un peu hors sujet, mais les bibliothèques de composants d’interface utilisateur pourraient être intéressantes pour certains lecteurs, qui recherchent des plugins:
https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e

NativeScript possède des plug-ins d’interface utilisateur intégrés:
https://docs.nativescript.org/plugins/building-plugins
Ces plugins ont besoin d’un wrapper angular:
https://docs.nativescript.org/plugins/angular-plugin