https://github.com/gioragutt/ngx-plugin-modules
An angular library for creating the perfect plugin-based architectures 🚀
https://github.com/gioragutt/ngx-plugin-modules
angular angular-lazy-loading angular-library architecture plugin-architecture plugins
Last synced: 9 months ago
JSON representation
An angular library for creating the perfect plugin-based architectures 🚀
- Host: GitHub
- URL: https://github.com/gioragutt/ngx-plugin-modules
- Owner: gioragutt
- Created: 2021-08-08T20:25:13.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2021-11-13T16:03:13.000Z (about 4 years ago)
- Last Synced: 2025-04-03T15:04:25.355Z (10 months ago)
- Topics: angular, angular-lazy-loading, angular-library, architecture, plugin-architecture, plugins
- Language: TypeScript
- Homepage: https://ngx-plugin-modules-demo.netlify.app
- Size: 1.76 MB
- Stars: 5
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# ngx-plugin-modules
[](https://app.netlify.com/sites/ngx-plugin-modules/deploys)
Docs: https://ngx-plugin-modules.netlify.app/
## TODO - REWRITE README
## Purpose
The purpose of this repository is to present a POC of a plugin system, and provide a way for these modules to expose API in a unified way.
PluginModules should be lazy-loadable. When lazy-loaded, they should be conditionally loaded (like `canLoad` with lazy loaded routes).
## Why not use the routing mechanism?
This is a POC which will hopefully, after refactoring and improving, be used in a certain app.
This app does not rely on routing, and basically has two routes: /login and /app.
The /app contains a big application, with a map, and a lot of map elements and floating forms.
Under certain circumstances, we don't want to load all feature modules -
- Different environments
- A/B Testing
- Etc
And so, I want to provide a way for modules to expose some api, that will be available to the app, if and once loaded.
## Structure
The main library is in `/projects/plugin-modules`.
It contains the entire plugin modules API, and exposes services which are needed to interact with, an plug into, the plugin module system.
The example plugin system is in `/projects/forms-registry`.
The purpose of the library is to save and expose a registry of components with certain attributes (e.g: each component has a category, code, etc.).
Each feature module can expose such components by using the `forms-registry` library.
## The Example Application
The app component shows a simple menu with all the components from the registry.
Clicking on a component name loads the component below the menu.
## Plugin System
Each plugin system can expose instances of the [PluginProcessor](projects/plugin-modules/src/lib/interfaces.ts#18) interface.
```typescript
// From library
export interface PluginProcessor {
process(moduleRef: NgModuleRef): void | Promise | Observable;
}
// Userland
export class MyProcessor implements PluginProcessor { ... }
NgModule({
providers: [
providePluginProcessor(MyProcessor),
],
})
class MyPluginSystemModule { ... }
```
Plugin systems can save whatever data they want on modules via providers.
When a feature module is loaded and boostrapped, it is ran through the `PluginProcessorsService` which saves all `PluginProcessors` and activates them.
When the plugin system processor is activated, this is where you can collect the data saved on the module, and act accordingly.
## The Example Plugin System: [Forms Registry](/projects/forms-registry)
The library exposes the `FormsRegistryService` which is the main public API for the plugin system:
```typescript
@Injectable()
export class FormsRegistry {
formEntries$(): Observable;
add(formEntry: FormEntry, componentFactory: ComponentFactory): void;
resolveComponentFactory(component: Type): ComponentFactory | null;
}
```
The plugin system registers the `PluginProcessor` via a `forRoot` import:
```typescript
@NgModule({
imports: [
PluginsModule.forRoot([
{
loadChildren: () => import('./feature1/feature1.module').then(m => m.Feature1Module),
name: 'feature1', // name provided for diagnostics
},
]),
FormsRegistryModule.forRoot(),
],
})
export class AppModule {}
```
Feature Modules expose forms to the registry via a `forFeature` import:
```typescript
@NgModule({
declarations: [Feature1FormComponent],
imports: [
CommonModule,
FormsRegistryModule.forFeature([
{
category: 'Category 1',
component: Feature1FormComponent,
name: 'Feature1FormComponent',
},
]), // expose api
PluginsModule.forFeature(), // register as module that exposes api via plugins
],
})
export class Feature1Module {}
```
When the module is loaded, the [FormsRegistryProcessor](projects/forms-registry/src/lib/forms-registry-processor.service.ts) collects the forms provided in the feature module, and loads them into the registry with `FormRegistryService#add`.