Implementing a plugin architecture / plugin system / pluggable framework in Angular 2, 4, 5, 6

后端 未结 12 580
伪装坚强ぢ
伪装坚强ぢ 2020-11-30 16:18

Update 5/24/2018: We are now +3 versions of Angular from my original post and still don\'t have a final workable solution. Lars Meijdam (@LarsMeijdam) has c

相关标签:
12条回答
  • 2020-11-30 16:45

    What you're looking for is lazy module loading. Here is an example of it: 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

    0 讨论(0)
  • 2020-11-30 16:46

    I was looking for a plugin 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 database-stored (but could be in the filesystem) pseudo-Angular components.

    The components stored in the database database are based on ng-dynamic and the main component implementation 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();
                    }
    
                });
    
            });
    
        }
    

    The 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 the templates of the components are like angular templates:

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

    And could be nested too:

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

    Although it's not perfect, the developers of the custom components have a similar framework than in angular2/4.

    0 讨论(0)
  • 2020-11-30 16:48

    It can be done, "manually". Since webpack do not know anything about external(plug-ins) module, he cannot include them in bundle(s). So what I did, is to look at the code generated by webpack and I found this pies of code in main.bundle.js:

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

    Lets examine what that array contains:

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

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

    0 讨论(0)
  • 2020-11-30 16:48

    I found a good article from Paul Ionescu about how to build a plugin extensible application in angular.

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

    He also references an example application at github: https://github.com/ionepaul/angular-plugin-architecture

    0 讨论(0)
  • 2020-11-30 16:50

    Update

    For Angular 11 I strongly recommend you to take a look at implementation with Webpack 5 Module Federation

    0 讨论(0)
  • 2020-11-30 16:52

    Example application with a working plugin system (thanks to Gijs for founding the github repo!) https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-10 based on the eBook Mastering Angular 2 Components

    • plugin architecture to extend core app components
    • file plugin system (for simply adding plugin directories/files without editing any core config files or the need to recompile your application!)
    • load and dynamically use plugins
    • building a rudimentary plugin manager to activate/deactive plugins on-the-fly

    Cheers, Niklas

    0 讨论(0)
提交回复
热议问题