Implement plugin / plugin / plugin framework architecture in Angular 2, Angular 4 or Angular 5

I would like to implement a plugin (plugin) structure in an Angular 2 , Angular 4 or Angular 5 application.

(My specific use case for developing this plug-in structure is that I need to develop a miniature content management system. For a number of reasons that will not necessarily be developed here, Angular 2/4/5 is almost an ideal solution for most of the needs of this system.)

Thanks to the plug-in structure (or plug-in architecture), I mean a system that allows third-party developers to create or extend the functionality of the main application using plug-in components, without having direct access or knowledge of the application’s source code or internal work.

(This wording about “without direct access to the source code of the application or its internal work” or “knowledge” is the main task.)

Examples of plug-in frameworks include common content management systems such as WordPress or Drupal .

The ideal situation (as in the case of Drupal) is to simply install these plug-in components (or plug-ins) into a folder, the application automatically detects or detects them, and they simply magically “work”. "If this happens in some kind of hot plug-in manner, that is, while the application is running, it would be optimal.

I am currently trying to determine the answers (with your help) to the following five questions.

  • Practicality: Are plugins for an Angular 2/4/5 application even practical? (So ​​far, I have not found a practical way to create a truly pluggable structure using Angular2/4/5 .)
  • Expected Tasks: What problems may arise when implementing a plugin for an Angular 2/4/5 application?
  • Implementation Strategies: What specific methods or strategies can be used to implement the plugin platform for the Angular 2/4/5 application?
  • Recommendations: What are the best methods for implementing a plug-in system for an Angular 2/4/5 application?
  • Alternative technologies: If the plugin is not applicable in the Angular 2/4/5 application, what relatively equivalent technologies (for example, React ) may be suitable for a modern highly responsive web application?

In general, using Angular 2/4/5 very desirable because:

  • it is naturally very fast - it is unbelievable.
  • it consumes very little bandwidth (after boot)
  • it has a relatively small area (after AOT and tree shaking ) - and this footprint continues to shrink
  • It is very functional, and the Angular team and community continue to grow rapidly in their ecosystem.
  • it works well with many of the best and latest web technologies such as TypeScript and Observables
  • Angular 5 now supports service workers ( https://medium.com/@webmaxru/a-new-angular-service-worker-creating-automatic-progressive-web-apps-part-1-theory-37d7d7647cc7 )
  • supported by Google , it is likely to be supported and improved in the future

I would really like to use Angular 2/4/5 for my current project. If I can use Angular 2/4/5 , I will also use Angular-CLI and possibly Angular Universal (for server-side rendering).

Here are my thoughts so far on the above issues. Please review and provide your feedback and education.

  • Angular 2/4/5 applications consume packages - but this is not necessarily the same as allowing plugins within the application. A plugin in other systems (for example, Drupal ) can be added by adding a plugin folder to the general modules directory, where the system is automatically "selected" by the system. In Angular 2/4/5 package (as a plugin can be) is usually installed via npm , added to package.json , and then manually imported into the application - as in app.module . This is much more complicated than the Drupal method for deleting a folder and the system automatically detects the package. The more difficult it is to install the plugin, the less likely it is that people will use them. It would be much better if there was an Angular 2/4/5 way to automatically detect and install plugins. I am very interested in finding a method that allows non-developers to install the Angular 2/4/5 application and install any selected plugins without having to understand the entire architecture of the application.

  • As a rule, one of the advantages of providing a plug-in architecture is that it is very easy for third-party developers to extend the functionality of the system. Obviously, these developers will not be familiar with all the complexities of the code for the application into which they are connected. After developing plugins, other even less technical users can simply install the application and any selected plugins. However, Angular 2/4/5 relatively complex and has a very long learning curve. To further complicate matters, most Angular 2/4/5 production applications also use Angular-CLI , Angular Universal and WebPack . Someone who implements the plugin should probably have at least some basic knowledge of how they all fit together - along with a strong working knowledge of TypeScript and a reasonable NodeJS . Are knowledge requirements so extreme that no third party will ever want to develop a plugin?

  • Most plugins will likely have some component on the server side (for example, for storing / receiving data related to plugins), as well as for output to the client side. Angular 2/4/5 specifically (and strongly) prevents developers from implementing their own templates at run time, as this poses a serious security risk. To handle the many types of output that a plug-in can connect (for example, displaying a graph), it seems that allowing users to create content that is entered into the response stream in one form is probably necessary. I wonder how it would be possible to satisfy this need without figuratively shredding Angular 2/4/5 security mechanisms.

  • Most Angular 2/4/5 production applications are precompiled using Ahead of Time ( AOT ) compilation. (Probably everything should be.) I'm not sure if plugins can be added to (or integrated) pre-compiled applications. A better scenario would involve compiling plugins separately from the main application. However, I am not sure how to do this. A failure may consist in recompiling the entire application with any plugins enabled, but this complicates the administrative user who just wants to install the application (on his own server) along with any selected plugins.

  • In an Angular 2/4/5 application, especially precompiled, one piece of erroneous or inconsistent code can break the entire application. Angular 2/4/5 applications are not always easier to debug. Using unscrupulous plugins can lead to very unpleasant experiences. Currently, I do not know about a mechanism for correctly handling plugins with incorrect behavior.

UPDATE:

11/22/2017. Now that Angular 5 has been released, I'm reviewing which new features can better facilitate the implementation of the plug-in infrastructure.

+51
plugins angular angular-cli angular-universal
Jan 03 '17 at 7:09
source share
6 answers

An example application with a working plug-in system (thanks to Gijs for creating github repo!) Https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-10 based on eBook Angular 2 component

  • to expand the core components of the application
  • file system (to easily add directories / plugin files without editing any basic configuration files or having to recompile your application!)
  • Download and dynamic use of plugins
  • creation of a rudimentary plug-in manager for activating / deactivating plug-ins on the fly

Cheers, Niklas

+4
Feb 06 '17 at 10:22
source share

What you are looking for is lazy module loading. Here is an example of this: 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; } } } 

Best ... Tom

+4
Feb 11 '17 at 22:25
source share

I made a hack to load and compiled other modules during boot, but I did not solve the cyclic dependency problem

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

then in AppModule add this:

 { provide: APP_INITIALIZER, useFactory: AppsLoaderFactory, deps: [AppsLoader], multi: true }, 
0
Jul 17 '17 at 18:49
source share

I was looking for a plug-in system in angular 2/4 too for developing a RAD environment for an enterprise application at work. After some research, I decided to implement a collection of pseudo-Angular components stored in the database (but may be in the file system).

The components stored in the database database are based on ng-dynamic and the implementation of the main component is similar to this:

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

External javascript code is similar to angular components:

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

And component templates are similar to angular templates:

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

And can also be nested:

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

Although not ideal, custom component developers have a similar structure than angular2 / 4.

0
Jul 18 '17 at 9:50
source share

This can be done manually. Since webpack does not know anything about the plug-ins module, it cannot include them in the package (s). So what I did was look at the code generated by webpack, and I found these code pies in main.bundle.js:

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

Let's look at what this array contains:

  • "./dashboard/dashboard.module" is the module routing URL we want to use for lazy loading: {path: 'dashboard', loadChildren: './dashboard/dashboard.module # DashboardModule'}
  • "../../../../../src/app/dashboard/dashboard.module.ts" - this is the entry point (contructor) takes from
  • "dashboard.module" is the actual file name without chunk.js (for example: dashboard.module.chunk.js)

So, theoretically, if you add an entry to the map property, configure your routing and follow the pattern, you can have a plug-in system. The challenge now is how to add or remove entries from this map property. Obviously this cannot be done from angular code, it must be done for an external tool.

0
Aug 11 '17 at 8:41 on
source share

I tried to implement the plugin architecture using ABP, Angular and ASP.NET Core: https://github.com/chanjunweimy/abp_plugin_with_ui

Basically, I developed Angular plugins using another Angular application, then I dynamically add them together.

Further information on how I achieve it:

I have an angular-cli application 2, 1 is the main Angular cli application, and the other is the Angular cli plugin. The problem we are facing with the angular-cli architecture approach is how we integrate them.

Right now, what I did was run ng-build on both applications and put them in the "wwwroot" folder, which was then hosted on an ASP.NET core 2.0 server. The simplest repository that shows this idea is Angular. Several applications: https://github.com/chanjunweimy/angular-multiple-app

abp_plugin_with_ui is a repository that works on creating a plugin containing both a backend and an Angular cli. For the backend, I used the aspnetboilerplate framework, whose interface was developed using several angular-cli applications.

For the main application to be integrated with the plugin application, we must run "ng-build" on both applications (note that we also need to change the plugin applications to href), then we move the built-in contents of the Angular cli plugin application to the main folder "wwwroot "applications. After all this has been achieved, we can run the “dotnet run” to serve the ASP.NET Core 2.0 web application to host the static files created by “ng build”.

Hope this helps. Any comments are welcome! ^^

0
Oct. 16 '17 at 2:45 on
source share



All Articles