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
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
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.
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:
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.
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
For Angular 11 I strongly recommend you to take a look at implementation with Webpack 5 Module Federation
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
Cheers, Niklas