style page ngstyle example change angular plugins angular-cli angular-universal

ngstyle - page title angular 4



Implementación de una arquitectura de complementos/sistema de complementos/marco conectable en Angular 2, 4, 5, 6 (12)

Acabo de publicar un nuevo capítulo para mi libro " Desarrollando con Angular " que aborda el tema de los complementos en Angular 2+ y debería ser de gran interés para las personas que intentan crear complementos externos.

Puntos clave:

  • Complementos
  • Crear componentes basados ​​en nombres de cadena
  • Cargando configuración desde fuentes externas
  • Cambio dinámico de rutas de aplicación
  • Complementos externos
  • Crear bibliotecas de complementos
  • Cargando complementos en la aplicación
  • Rutas dinámicas con contenido de complementos

El libro es gratuito y tiene el modelo "paga lo que quieras". Siéntase libre de tomar una copia y espero que eso ayude.

Actualización 24/05/2018: ahora somos versiones +3 de Angular de mi publicación original y todavía no tenemos una solución viable final. Lars Meijdam (@LarsMeijdam) ha presentado un enfoque interesante que vale la pena ver. (Debido a problemas de propiedad, tuvo que eliminar temporalmente el repositorio de GitHub donde había publicado originalmente su muestra. Sin embargo, puede enviarle un mensaje directamente si desea una copia. Consulte los comentarios a continuación para obtener más información).

Los cambios arquitectónicos recientes en Angular 6 nos acercan a una solución. Además, Angular Elements ( https://angular.io/guide/elements ) proporciona algunas funciones de componentes, aunque no exactamente lo que describí originalmente en esta publicación.

Si alguien del increíble equipo Angular se encuentra con esto, tenga en cuenta que parece haber muchas otras personas que también están muy interesadas en esta funcionalidad. Bien podría valer la pena considerar para el trabajo atrasado.

Me gustaría implementar un marco conectable (plug-in) en una aplicación Angular 2 , Angular 4 , Angular 5 o Angular 6 .

(Mi caso de uso específico para desarrollar este marco conectable es que necesito desarrollar un sistema de administración de contenido en miniatura. Por una serie de razones no necesariamente elaboradas aquí, Angular 2/4/5/6 es casi perfecto para la mayoría de las necesidades de ese sistema).

Por marco conectable (o arquitectura de complemento), me refiero específicamente a un sistema que permite a los desarrolladores de terceros crear o ampliar la funcionalidad de una aplicación primaria mediante el uso de componentes conectables sin tener acceso directo o conocimiento del código fuente de la aplicación primaria o funcionamiento interno .

(Esa frase sobre " sin tener acceso directo o conocimiento del código fuente de la aplicación o el funcionamiento interno " es un objetivo central).

Los ejemplos de marcos conectables incluyen sistemas comunes de administración de contenido como WordPress o Drupal .

La situación ideal (como con Drupal) sería simplemente poder colocar estos componentes enchufables (o complementos) en una carpeta, hacer que la aplicación los detecte automáticamente o los descubra, y hacer que simplemente "funcionen" mágicamente. Hacer que esto ocurra de alguna manera conectable en caliente, lo que significa que mientras la aplicación se estaba ejecutando, sería óptimo.

Actualmente estoy tratando de determinar las respuestas ( con su ayuda ) a las siguientes cinco preguntas.

  1. Practicidad: ¿Es práctico un complemento para una aplicación Angular 2/4/5/6 ? (Hasta ahora, no he encontrado ninguna forma práctica de crear un marco verdaderamente conectable con Angular2/4/5/6 ).
  2. Desafíos esperados: ¿Qué desafíos podría enfrentar uno al implementar un marco de plugin para una aplicación Angular 2/4/5/6 ?
  3. Estrategias de implementación: ¿Qué técnicas o estrategias específicas podrían emplearse para implementar un marco de plugin para una aplicación Angular 2/4/5/6 ?
  4. Mejores prácticas: ¿Cuáles son las mejores prácticas para implementar un sistema de complemento para una aplicación Angular 2/4/5/6 ?
  5. Tecnologías alternativas: si un marco de plugin no es práctico en una aplicación Angular 2/4/5/6 , ¿qué tecnologías relativamente equivalentes (por ejemplo, React ) podrían ser adecuadas para una aplicación web moderna altamente reactiva ?

En general, el uso de Angular 2/4/5/6 es muy deseable porque:

  • es naturalmente extremadamente rápido, increíblemente rápido.
  • consume muy poco ancho de banda (después de la carga inicial)
  • tiene una huella relativamente pequeña (después de AOT y tree shaking ), y esa huella continúa disminuyendo
  • es altamente funcional, y el equipo y la comunidad de Angular continúan creciendo rápidamente en su ecosistema
  • funciona bien con muchas de las mejores y más recientes tecnologías web, como TypeScript y Observables
  • Angular 5 ahora es compatible con trabajadores de servicios ( https://medium.com/@webmaxru/a-new-angular-service-worker-creating-automatic-progressive-web-apps-part-1-theory-37d7d7647cc7 )
  • respaldado por Google , es probable que sea compatible y mejorado en el futuro

Me gustaría mucho usar Angular 2/4/5/6 para mi proyecto actual. Si puedo usar Angular 2/4/5/6 , también Angular 2/4/5/6 Angular-CLI y probablemente Angular Universal (para la representación del lado del servidor).

Aquí están mis pensamientos, hasta ahora, con respecto a las preguntas anteriores. Por favor revise y proporcione sus comentarios e iluminación.

  • Angular 2/4/5/6 aplicaciones Angular 2/4/5/6 consumen paquetes, pero esto no es necesariamente lo mismo que permitir complementos dentro de una aplicación. Un complemento en otros sistemas (por ejemplo, Drupal ) se puede agregar esencialmente soltando la carpeta del complemento en un directorio de módulos común donde el sistema lo "recoge" automáticamente. En Angular 2/4/5/6 , un paquete (como podría ser un complemento) generalmente se instala a través de npm , se agrega a package.json y luego se importa manualmente a la aplicación, como en app.module . Esto es mucho más complicado que el método Drupal de soltar una carpeta y hacer que el sistema detecte automáticamente el paquete. Cuanto más complicado sea instalar un complemento, menos probable será que las personas los usen. Sería mucho mejor si Angular 2/4/5/6 hubiera una forma de detectar e instalar complementos automáticamente. Estoy muy interesado en encontrar un método que permita a los no desarrolladores instalar la aplicación Angular 2/4/5/6 e instalar cualquier complemento elegido sin tener que comprender toda la arquitectura de la aplicación.

  • En general, uno de los beneficios de proporcionar una arquitectura conectable es que es muy fácil para los desarrolladores externos ampliar la funcionalidad del sistema. Obviamente, estos desarrolladores no estarán familiarizados con todas las complejidades del código para la aplicación a la que se están conectando. Una vez que se desarrollan los complementos, otros usuarios aún menos técnicos pueden simplemente instalar la aplicación y los complementos seleccionados. Sin embargo, Angular 2/4/5/6 es relativamente complicado y tiene una curva de aprendizaje muy larga. Para complicar aún más las cosas, la mayoría de las aplicaciones de producción Angular 2/4/5/6 también utilizan Angular-CLI , Angular Universal y WebPack . Alguien que está implementando un complemento probablemente tendría que tener al menos un conocimiento básico de cómo encajan todos estos elementos, junto con un sólido conocimiento práctico de TypeScript y una familiaridad razonable con NodeJS . ¿Los requisitos de conocimiento son tan extremos que ningún tercero querría desarrollar un complemento?

  • Es probable que la mayoría de los complementos tengan algún componente del lado del servidor (por ejemplo, para almacenar / recuperar datos relacionados con el complemento), así como algunos resultados del lado del cliente. Angular 2/4/5 específicamente (y fuertemente) desalienta a los desarrolladores de inyectar sus propias plantillas en tiempo de ejecución, ya que esto plantea un grave riesgo de seguridad. Con el fin de manejar muchos tipos de salida que puede acomodar un complemento (por ejemplo, la visualización de un gráfico), parece que probablemente sea necesario permitir a los usuarios crear contenido que se inyecta en la secuencia de respuesta, de una forma u otra. Me pregunto cómo podría ser posible satisfacer esta necesidad sin Angular 2/4/5/6 sentido figurado los mecanismos de seguridad de Angular 2/4/5/6 .

  • La mayoría de las aplicaciones de producción Angular 2/4/5/6 se compilan previamente utilizando la compilación Ahead of Time ( AOT ). (Probablemente todos deberían estarlo). No estoy seguro de cómo se pueden agregar complementos a (o integrar con) aplicaciones precompiladas. El mejor escenario implicaría compilar los complementos por separado de la aplicación principal. Sin embargo, no estoy seguro de cómo hacer que esto funcione. Un inconveniente podría ser volver a compilar toda la aplicación con los complementos incluidos, pero eso complica un poco las cosas para un usuario administrativo que simplemente quiere instalar la aplicación (en su propio servidor) junto con los complementos seleccionados.

  • En una aplicación Angular 2/4/5/6 , especialmente una precompilada, una sola pieza de código errante o conflictivo puede romper toda la aplicación. Angular 2/4/5/6 aplicaciones Angular 2/4/5/6 no siempre son las más fáciles de depurar. La aplicación de complementos de mal comportamiento podría dar lugar a experiencias muy desagradables. Actualmente no conozco un mecanismo para manejar con gracia los complementos de mal comportamiento.


Actualmente estoy en la misma búsqueda que tú, tratando de hacer una versión conectable / temática de Angular, y no es un problema trivial.

En realidad, encontré soluciones bastante buenas, leyendo el libro Desarrollando con Angular por el genio Denys Vuyika , en realidad en el libro explica una solución bastante buena, habla sobre complementos externos en la página 356 del libro y usa Rollup.js para lograr el solución, luego procesa para cargar dinámicamente complementos externos que se han creado previamente fuera de su aplicación.

También hay otras dos bibliotecas / proyectos que lo ayudan a lograr este resultado ng-packagr y extensiones Nx para Agnular (de Nrwl) que estamos tratando de implementar este último, y yo diría que no es tan sencillo como anticipamos, angular fue simple no está diseñado para eso, por lo que tenemos que trabajar en torno al núcleo sobre cómo Angular, y las personas NX son una de las mejores.

Solo estamos al comienzo de nuestro proyecto de código abierto, estamos usando Django + Mongo + Angular (estamos llamando a WebDjangular y uno de nuestros posibles enfoques para esta respuesta es que Django tendrá que escribir algunos archivos de configuración JSON y construir el aplicación cada vez que se instala y activa un nuevo complemento o tema.

Lo que ya logramos es que, desde la base de datos, podemos usar etiquetas para los componentes como en el complemento, ¡y el componente se imprimirá en la pantalla! Una vez más, el proyecto se encuentra en etapas muy tempranas, basamos un poco nuestra arquitectura en Wordpress y tenemos muchas más pruebas que hacer para lograr nuestro sueño: D

Espero que el Libro pueda ayudarlo, y usando Rollup.js sé que podrá resolver este problema no trivial.


Creé un repositorio en github con una solución que podría ayudar. Utiliza las bibliotecas Angular 6 y 1 aplicaciones base que cargan las bibliotecas agrupadas UMD de forma perezosa; github.com/lmeijdam/angular-umd-dynamic-example

Si tiene alguna sugerencia, ¡no dude en agregarla!


Ejemplo de aplicación con un sistema de complemento que funciona (¡gracias a Gijs por fundar el repositorio de github!) https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-10 based en el eBook Mastering Angular 2 Components

  • arquitectura de complementos para ampliar los componentes principales de la aplicación
  • sistema de complementos de archivos (¡para simplemente agregar directorios / archivos de complementos sin editar ningún archivo de configuración central o la necesidad de recompilar su aplicación!)
  • cargar y usar dinámicamente complementos
  • construir un administrador de complementos rudimentario para activar / desactivar complementos sobre la marcha

Saludos, Niklas



Estaba buscando un sistema de complemento en angular 2/4 también para desarrollar un entorno RAD para una aplicación empresarial en el trabajo. Después de investigar un poco, decidí implementar una colección de componentes pseudo-angulares almacenados en la base de datos (pero podría estar en el sistema de archivos).

Los componentes almacenados en la base de datos de la base de datos se basan en ng-dynamic y la implementación del componente principal es similar a esto:

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(); } }); }); }

El código externo de JavaScript es similar a los componentes angulares:

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''; };

Y las plantillas de los componentes son como plantillas angulares:

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

Y también podría estar anidado:

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

Aunque no es perfecto, los desarrolladores de los componentes personalizados tienen un marco similar al de angular2 / 4.


Hice un truco para cargar y compilar otros módulos en tiempo de arranque, pero no he resuelto el problema de las dependencias cíclicas.

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));

luego en AppModule agregue esto:

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


Lo que está buscando es la carga lenta del módulo. Aquí hay un ejemplo de ello: 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; } } }

Mejor ... Tom


Se puede hacer, "manualmente". Como webpack no sabe nada sobre el módulo externo (complementos), no puede incluirlos en los paquetes. Entonces, lo que hice fue mirar el código generado por webpack y encontré estos pasteles de código en main.bundle.js:

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

Examinemos qué contiene esa matriz:

  1. "./dashboard/dashboard.module" : esta es la URL de enrutamiento del módulo que queremos cargar de forma diferida, por ejemplo: {ruta: ''tablero'', loadChildren: ''./dashboard/dashboard.module#DashboardModule''}
  2. "../../../../../src/app/dashboard/dashboard.module.ts" - este es el punto de entrada (contructor) toma de
  3. "dashboard.module" : nombre real del archivo sin chunk.js (por ejemplo: dashboard.module.chunk.js )

Entonces, en teoría, si agrega una entrada a la propiedad del mapa, configure su enrutamiento y siga el patrón, puede tener un sistema de complemento. Ahora el desafío es cómo agregar o eliminar entradas de esa propiedad del mapa. Obviamente no se puede hacer desde un código angular, se debe hacer desde una herramienta externa.


Traté de implementar una arquitectura de complementos utilizando ABP, Angular y ASP.NET Core: https://github.com/chanjunweimy/abp_plugin_with_ui

Básicamente, desarrollé complementos angulares utilizando diferentes aplicaciones angulares, luego los agregué dinámicamente.

Más información sobre cómo lo logro:

Tengo 2 aplicaciones angular-cli, 1 es la aplicación principal angular cli, y otra es la aplicación de complemento angular cli. El problema que enfrentamos en el enfoque de arquitectura de plugin Angular-cli es cómo los integramos.

En este momento, lo que hice fue ejecutar ng-build en ambas aplicaciones y colocarlas en una carpeta "wwwroot", que luego se alojó en un servidor ASP.NET core 2.0. Un repositorio más simple que muestra esta idea es Angular Multiple App: https://github.com/chanjunweimy/angular-multiple-app

abp_plugin_with_ui es un repositorio que funciona en el desarrollo de un complemento que contiene tanto el backend como Angular cli. Para el backend, utilicé el marco aspnetboilerplate, que se desarrolla mediante la aplicación múltiple angular-cli.

Para tener la aplicación principal integrada con la aplicación del complemento, tenemos que ejecutar "ng-build" en ambas aplicaciones (tenga en cuenta que también tenemos que cambiar a href de la aplicación del complemento), luego movemos los contenidos compilados del complemento aplicación angular cli, a la carpeta principal de la aplicación "wwwroot". Después de lograr todo esto, podemos ejecutar "dotnet run" para servir la aplicación web ASP.NET Core 2.0 para alojar los archivos estáticos generados por "ng build".

Espero que ayude. Cualquier comentario es bienvenido! ^^


Un poco fuera de tema, pero las bibliotecas de componentes de la interfaz de usuario podrían ser de interés para algunos de los lectores, que buscan complementos:
https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e

NativeScript tiene complementos de interfaz de usuario integrados:
https://docs.nativescript.org/plugins/building-plugins
Esos complementos necesitan un envoltorio angular:
https://docs.nativescript.org/plugins/angular-plugin


🛠️ Github demo angular-plugin-architecture

Tal vez Ivy pueda cambiar algo, pero por el momento utilizo la solución que usa Angular CLI Custom Builder y cumple con los siguientes requisitos:

  • Mucho
  • evite el código duplicado (paquetes como @ angular / core {común, formularios, enrutador}, rxjs, tslib)
  • use la biblioteca compartida en todos los complementos, pero NO ENVÍE las fábricas generadas a partir de esa biblioteca compartida en cada complemento, sino que reutilice el código y las fábricas de la biblioteca
  • el mismo nivel de optimización que Angular CLI nos brinda
  • para importar los módulos externos solo necesitamos saber una cosa: la ruta del archivo de paquete
  • nuestro código debe reconocer el módulo y colocar el complemento en la página
  • soporte de representación del lado del servidor
  • cargar el módulo solo cuando sea necesario

El uso es simple como:

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

Más sobre esto en mi artículo: