web-dev-qa-db-fra.com

Implémentation d'une architecture / système de plugin / framework plug-in dans Angular 2, 4, 5, 6

Mise à jour du 24/05/2018: Nous sommes maintenant à la version +3 de Angular de mon message d'origine et nous n'avons toujours pas de solution finale réalisable. 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û retirer temporairement le référentiel GitHub où il avait posté son échantillon. Toutefois, vous pouvez lui envoyer directement un message si vous souhaitez en obtenir une copie. Veuillez consulter les commentaires ci-dessous pour plus d'informations.)

Les récents changements architecturaux dans Angular 6 nous rapprochent d'une solution. De plus, Angular Elements ( --- (https://angular.io/guide/elements ) fournit des fonctionnalités de composant - bien que ce ne soit pas tout à fait ce que j'avais décrit à l'origine dans ce message.

Si quelqu'un de l'incroyable équipe Angular rencontre ce problème, veuillez noter qu'il semble y avoir beaucoup d'autres personnes également intéressées par cette fonctionnalité. Il serait peut-être utile de prendre en compte l'arriéré.


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

(Mon cas d'utilisation spécifique pour développer ce cadre enfichable est que je dois développer un système de gestion de contenu miniature. Pour un certain nombre de raisons, pas nécessairement expliquées ici, Angular 2/4/5/6 correspond parfaitement à la plupart des besoins de ce système. .)

Par cadre enfichable (ou architecture de plug-in), j'entends en particulier un système qui permet aux développeurs tiers de créer ou d'étendre les fonctionnalités d'une application principale en utilisant des composants enfichables sans accès direct à ou connaissance du code source ou du fonctionnement interne de l'application principale .

(Cette formulation à propos de " sans avoir un accès direct au code source de l'application ou une connaissance de son fonctionnement interne " est un objectif fondamental.)

Les exemples de frameworks enfichables incluent les systèmes de gestion de contenu courants tels que WordPress ou Drupal.

La situation idéale (comme dans Drupal) serait de pouvoir simplement placer ces composants enfichables (ou plug-ins) dans un dossier, faire en sorte que l’application les détecte ou les détecte automatiquement et les fasse "fonctionner" comme par magie. Avoir cela se produise d'une manière connectable à chaud, ce qui signifie que pendant le fonctionnement de l'application, serait optimal.

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

  1. En pratique: Un framework de plug-in pour une application Angular 2/4/5/6 est-il même pratique? (Jusqu'à présent, je n'ai trouvé aucun moyen pratique de créer un cadre véritablement enfichable avec Angular2/4/5/6.)
  2. Défis attendus: Quels problèmes peut-on rencontrer lors de la mise en œuvre d'un framework de plug-in pour une application Angular 2/4/5/6?
  3. Stratégies d'implémentation: Quelles techniques ou stratégies spécifiques pourrait-on employer 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 la mise en œuvre d'un système de plug-in pour une application Angular 2/4/5/6?
  5. Technologies alternatives: Si un framework de plugin est pas pratique dans une application Angular 2/4/5/6, quelles technologies relativement équivalentes (par exemple, React) pourraient convenir à une moderne hautement réactif Application Web?

En général, l'utilisation de Angular 2/4/5/6 est très souhaitable pour les raisons suivantes:

  • il est naturellement extrêmement rapide - extrêmement.
  • il consomme très peu de bande passante (après le chargement initial)
  • son encombrement est relativement faible (après AOT et tree shaking) - et cet encombrement continue de se réduire
  • il est hautement fonctionnel et l’équipe et la communauté Angular continuent de développer rapidement leur écosystème
  • il fonctionne 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 prestataires de 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é bien à l'avenir

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 ce que je pense à ce jour des questions ci-dessus. S'il vous plaît examiner et fournir vos commentaires et illumination.

  • Angular 2/4/5/6 les applications consomment des packages - mais ceci n'est pas nécessairement identique à autoriser les plugins dans une application. Un plug-in dans d'autres systèmes (par exemple, Drupal) peut être essentiellement ajouté en déposant le dossier du plug-in dans un répertoire de modules commun où il est automatiquement "récupéré" par le système. Dans Angular 2/4/5/6, un paquet (comme un plugin peut être) est généralement installé via npm, ajouté au package.json, puis importé manuellement dans l'application - comme dans app.module. Cette opération est beaucoup plus compliquée que la méthode Drupal pour supprimer un dossier et permettre au système de détecter automatiquement le package. Plus l'installation d'un plugin est compliquée, moins il est probable que des personnes l'utilisent. Il serait beaucoup mieux s'il y avait un moyen pour Angular 2/4/5/6 de s'automatiser détecter et installer des 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 plug-ins choisis sans avoir à comprendre toute l'architecture de l'application.

  • Généralement, l'un des avantages d'une architecture enfichable est qu'il est très facile pour les développeurs tiers d'étendre les fonctionnalités du système. Évidemment, ces développeurs ne connaîtront pas toutes les subtilités du code de 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 de production Angular 2/4/5/6 utilisent également Angular-CLI, Angular Universal et WebPack. Quelqu'un qui implémente un plugin devrait probablement avoir au moins quelques connaissances de base sur la manière dont tout cela s’allait bien - ainsi qu’une solide connaissance pratique de TypeScript et une connaissance raisonnable de NodeJS. Les exigences en matière de connaissances sont-elles si extrêmes qu'aucun tiers ne voudrait développer un plugin?

  • La plupart des plugins auront probablement un composant côté serveur (par exemple, pour stocker/récupérer les données associées au plugin), ainsi que des sorties côté client. Angular 2/4/5 décourage spécifiquement (et fortement) les développeurs d'injecter leurs propres modèles au moment de l'exécution, car cela pose un risque sérieux pour la sécurité. Afin de gérer de nombreux types de sorties pouvant être prises en charge par un plug-in (affichage d'un graphique, par exemple), il est probablement nécessaire de permettre aux utilisateurs de créer du contenu injecté dans le flux de réponses. Je me demande comment il serait possible de répondre à ce besoin sans détruire de manière figurative les mécanismes de sécurité de Angular 2/4/5/6.

  • La plupart des applications de production Angular 2/4/5/6 sont pré-compilées à l'aide de la compilation Ahead of Time (AOT). (Probablement tous devraient l'être.) Je ne sais pas comment les plugins pourraient être ajoutés à (ou intégrés à) des applications précompilées. Le meilleur scénario consisterait à compiler les plug-ins séparément de l'application principale. Cependant, je ne sais pas comment faire pour que cela fonctionne. Une solution de rechange pourrait consister à recompiler l’application entière avec tous les plugins inclus, mais cela complique un peu les choses pour un utilisateur administratif 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 élément de code erroné ou conflictuel peut détruire l'application entière. Angular 2/4/5/6 les applications ne sont pas toujours les plus faciles à déboguer. L'application de plugins mal élevés pourrait entraîner des expériences très désagréables. Je ne suis actuellement pas au courant d'un mécanisme permettant de gérer avec élégance des plugins mal élevés.

116
Anthony Gatlin

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

Si vous avez des suggestions, n'hésitez pas à les ajouter!

14
Lars Meijdam

Je viens de publier un nouveau chapitre pour mon livre " Developing with Angular " qui aborde le sujet des plugins dans Angular 2+ et devrait présenter un grand intérêt pour ceux qui essaient construire des plugins externes.

Points clés:

  • Plugins
  • Construction de composants basés sur des noms de chaîne
  • Chargement de la configuration à partir de sources externes
  • Modification dynamique des itinéraires d'application
  • Plugins externes
  • Création de bibliothèques de plugins
  • Chargement de plugins dans l'application
  • Routes dynamiques avec contenu de plugin

Le livre est gratuit et comprend le modèle "payez ce que vous voulez". N'hésitez pas à en prendre un exemplaire et espérez que cela vous aidera.

10
Denys Vuika

Exemple d’application avec un système de plugins opérationnel (merci à Gijs d’avoir fondé le référentiel github!) https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components- chapitre-1 basé sur le livre électronique Mastering Angular 2 composants

  • architecture de plugin pour étendre les composants principaux de l'application
  • système de plug-in de fichiers (pour ajouter simplement des répertoires/fichiers de plug-in sans éditer de fichier de configuration principal ni recompiler votre application!)
  • charger et utiliser dynamiquement les plugins
  • construire un gestionnaire de plugins rudimentaire pour activer/désactiver les plugins à la volée

À la vôtre, Niklas

6
Niklas Teich

Ce que vous cherchez, c'est un chargement de module paresseux. 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: `
    <a [routerLink]="['/']">Home</a> | 
    <a [routerLink]="['/app/home']">App Home</a> |
    <a [routerLink]="['/app/lazy']">App Lazy</a>

    <hr>
    <button (click)="addRoutes()">Add Routes</button>

    <hr>
    <router-outlet></router-outlet>
  `
})
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

5
Tom I

???? Démo Github angular-plugin-architecture

Peut-être qu'Ivy peut changer quelque chose, mais pour le moment j'utilise la solution qui utilise Angular CLI Custom Builder et qui répond aux exigences suivantes:

  • AOT
  • éviter le code en double (paquets tels que @ angular/core {commun, formulaires, routeur}, rxjs, tslib)
  • utiliser la bibliothèque partagée dans tous les plugins, mais NE PAS ENVOYER les fabriques générées à partir de cette bibliothèque partagée dans chaque plugin, mais plutôt réutiliser le code de la bibliothèque et les fabriques.
  • le même niveau d'optimisation que Angular CLI nous donne
  • pour importer les modules externes, nous n'avons besoin que d'une seule chose: leur chemin d'accès au fichier bundle
  • notre code devrait reconnaître le module et placer le plugin dans la page
  • supporte le rendu côté serveur
  • charger le module uniquement en cas de besoin

L'utilisation est simple comme:

ng build --project plugins --prod --modulePath=./plugin1/plugin1.module#Plugin1Module 
         --pluginName=plugin1 --sharedLibs=shared --outputPath=./src/assets/plugins

Plus à ce sujet dans mon article:

5
yurzui

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é angular plugins en utilisant différentes applications angular, puis je les ajoute de manière dynamique.

Plus d'informations sur comment y parvenir:

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

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

abp_plugin_with_ui est un référentiel permettant de développer un plug-in contenant à la fois le backend et Angular cli. Pour le backend, j'ai utilisé le framework aspnetboilerplate, dont l'interface est développée en utilisant plusieurs applications angular-cli.

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

J'espère que ça aide. Tous les commentaires sont les bienvenus! ^^

2
Junwei Chan

je suis actuellement dans la même quête que vous, essayant de créer une version plugable/thémable de Angular, et ce n'est pas un problème trivial.

En fait, j'ai trouvé de très bonnes solutions, en lisant le livre Développer avec Angular par le génie Denys Vuyika , il explique en fait sur le livre une très bonne solution, il parle de plugins externes la page 356 du livre et des utilisations Rollup.js pour obtenir la solution, il procède ensuite au chargement dynamique des plug-ins externes précédemment créés en dehors de votre application.

Il existe également deux autres bibliothèques/projets qui vous aident à atteindre ce résultat ng-packagr et extensions Nx pour Agnular (de Nrwl) nous essayons d'implémenter ce dernier, et i Je dirais que ce n’est pas aussi fluide que nous l’avions prévu, angular était simple, pas construit pour cela, nous devons donc travailler sur le cœur de la façon dont Angular, et les ppl NX sont l’un des meilleurs sur il.

Nous n'en sommes qu'au début de notre projet Open Source, nous utilisons Django + Mongo + Angular, (Nous appelons WebDjangular et l'une de nos approches possibles pour cette réponse est que Django devra écrire des fichiers de configuration JSON et construire l’application chaque fois qu’un nouveau plugin ou thème est installé et activé.

Ce que nous avons déjà accompli, c’est que depuis la base de données, nous pouvons utiliser des balises pour les composants comme sur le plugin, et le composant sera imprimé à l’écran! Encore une fois, le projet en est à ses débuts, nous basons un peu notre architecture sur Wordpress, et nous avons beaucoup plus de tests à faire pour réaliser notre rêve: D

J'espère que le livre peut vous aider, et en utilisant Rollup.js, je sais que vous pourrez résoudre ce problème non trivial.

2

J'ai fait un hack pour charger et compiler d'autres modules en bootstrap time, 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<any> => module
 });
 promises.Push(this.compiler.compileModuleAndAllComponentsAsync(module));

puis dans AppModule, ajoutez ceci:

{
        provide: APP_INITIALIZER,
        useFactory: AppsLoaderFactory,
        deps: [AppsLoader],
        multi: true
},
2
Jose Luis Berrocal

Cela peut être fait "manuellement". Étant donné que Webpack ne sait 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 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" - il s'agit de l'URL de routage du module avec lequel nous voulons charger paresseux, par exemple: {chemin: 'tableau de bord', loadChildren : './dashboard/dashboard.module#DashboardModule'}
  2. "../../../../../ src/app/dashboard/dashboard.module.ts" - c'est le point d'entrée (le constructeur) prend de
  3. "dashboard.module" - nom de fichier actuel sans chunk.js (par exemple: dashboard.module.chunk.js )

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

2
Niki Uzun

Un peu hors sujet, mais les bibliothèques de composants d'interface utilisateur pourraient intéresser 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 a des plugins d'interface utilisateur intégrés:
https://docs.nativescript.org/plugins/building-plugins
Ces plugins ont besoin d’un Angular Wrapper:
https://docs.nativescript.org/plugins/angular-plugin

2
RaSor

Je cherchais également un système de plug-in dans angular 2/4 pour le développement d'un environnement RAD pour une application d'entreprise au travail. Après quelques recherches, j'ai décidé d'implémenter une collection de composants pseudo-angulaires stockés dans la base de données (mais pouvant se trouver dans le système de fichiers).

Les composants stockés dans la base de données sont basés sur ng-dynamic et l'implémentation du composant principal est similaire à celle-ci:

declare var ctx: any;

@Component({
    selector: 'my-template',
    template: `
<div>
    <div *dynamicComponent="template; context: { ctx: ctx };"></div>
</div>
  `,
    providers: [EmitterService],

})

export class MyTemplateComponent implements OnMount, AfterViewInit, OnChanges {


    // name
    private _name: string;
    get name(): string {
        return this._name;
    }
    @Input()
    set name(name: string) {
        this._name = name;        
        this.initTemplate();
    }

    template: string;
    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 angular:

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: string = 'other-component'; 

};

Et les templates des composants ressemblent à angular templates:

<a (click)="customFunction1()">{{ ctx.model.title }}</a>
<input [(ngModel)]="ctx.model.title" type="text" />

Et pourrait être imbriqué aussi:

<a (click)="customFunction1()">{{ ctx.model.title }}</a>
<my-template [name]="childTemplateName"></my-template>

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

2
zarpilla

J'ai trouvé un bon article de Paul Ionescu sur la façon de construire une application extensible de plugin en mode angulaire.

https://itnext.io/how-to-build-a-plugin-extensible-application-architecture-in-angular5-736890278f3f

Il référence également un exemple d'application sur github: https://github.com/ionepaul/angular-plugin-architecture

0
Marvin Caspar