{"id":13626523,"url":"https://github.com/worktile/ngx-planet","last_synced_at":"2025-04-08T04:09:45.372Z","repository":{"id":37655692,"uuid":"165048843","full_name":"worktile/ngx-planet","owner":"worktile","description":"🚀🌍🚀A powerful, reliable, fully-featured and production ready Micro Frontend library for Angular.","archived":false,"fork":false,"pushed_at":"2025-03-31T02:53:49.000Z","size":14904,"stargazers_count":545,"open_issues_count":14,"forks_count":68,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-04-01T03:24:12.579Z","etag":null,"topics":["angular","micro","microfront","microfrontends","microfrontends-demo","ngx-planet","portal"],"latest_commit_sha":null,"homepage":"http://planet.ngnice.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/worktile.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2019-01-10T11:35:30.000Z","updated_at":"2025-03-31T03:20:25.000Z","dependencies_parsed_at":"2024-03-17T06:24:06.447Z","dependency_job_id":"b7d527e1-6ed0-4f44-b1d8-cbc08a7a427e","html_url":"https://github.com/worktile/ngx-planet","commit_stats":null,"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fngx-planet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fngx-planet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fngx-planet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fngx-planet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/worktile","download_url":"https://codeload.github.com/worktile/ngx-planet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247773711,"owners_count":20993634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","micro","microfront","microfrontends","microfrontends-demo","ngx-planet","portal"],"created_at":"2024-08-01T21:02:21.538Z","updated_at":"2025-04-08T04:09:45.338Z","avatar_url":"https://github.com/worktile.png","language":"TypeScript","readme":"# ngx-planet\n\n[![CircleCI](https://circleci.com/gh/worktile/ngx-planet.svg?style=shield)](https://circleci.com/gh/worktile/ngx-planet)\n[![Coverage Status][coveralls-image]][coveralls-url]\n[![npm (scoped)](https://img.shields.io/npm/v/@worktile/planet?style=flat)](https://www.npmjs.com/package/@worktile/planet)\n[![npm](https://img.shields.io/npm/dm/@worktile/planet)](https://www.npmjs.com/package/@worktile/planet)\n![npm bundle size (scoped)](https://img.shields.io/bundlephobia/min/@worktile/planet) [![All Contributors](https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square)](#contributors-)\n\n[coveralls-image]: https://coveralls.io/repos/github/worktile/ngx-planet/badge.svg?branch=master\n[coveralls-url]: https://coveralls.io/github/worktile/ngx-planet\n\nA powerful, reliable, fully-featured and production ready Micro Frontend library for Angular.\n\nAPIs consistent with angular style, currently only supports Angular, other frameworks are not supported.\n\nEnglish | [中文文档](https://github.com/worktile/ngx-planet/blob/master/README.zh-CN.md)\n\n## ✨ Features\n\n- Rendering multiple applications at the same time\n- Support two mode, coexist and default that switch to another app and destroy active apps\n- Support application preload\n- Support style isolation\n- Built-in communication between multiple applications\n- Cross application component rendering\n- Comprehensive examples include routing configuration, lazy loading and all features\n\n## 📖 Documentation\n- [Introduce](http://planet.ngnice.com/guides/intro)\n- [Getting Started](http://planet.ngnice.com/guides/getting-started)\n- [Development and Build](http://planet.ngnice.com/guides/dev-build)\n- [Data shared and Communication](http://planet.ngnice.com/guides/communication)\n- [Cross Application Component rendering](http://planet.ngnice.com/guides/cross-app-comp-rendering)\n- [API References](http://planet.ngnice.com/guides/api)\n\n## Alternatives\n\n-   [single-spa](https://github.com/CanopyTax/single-spa): A javascript front-end framework supports any frameworks.\n-   [mooa](https://github.com/phodal/mooa): A independent-deployment micro-frontend Framework for Angular from single-spa, `planet` is very similar to it, but `planet` is more powerful, reliable, productively and more angular.\n\n## Installation\n\n```bash\n$ npm i @worktile/planet --save\n// or\n$ yarn add @worktile/planet\n```\n\n## Demo\n\n[Try out our live demo](http://planet-examples.ngnice.com)\n\n![ngx-planet-micro-front-end.gif](https://cdn.pingcode.com/open-sources/ngx-planet/ngx-planet-micro-front-end.gif)\n\n## Usage\n\n### 1. Loading NgxPlanetModule in the portal's AppModule\n\n```\nimport { NgxPlanetModule } from '@worktile/planet';\n\n@NgModule({\n  imports: [\n    CommonModule,\n    NgxPlanetModule\n  ]\n})\nclass AppModule {}\n```\n\n### 2. Register applications to planet use PlanetService in portal app\n\n```ts\n@Component({\n    selector: 'app-portal-root',\n    template: `\n        \u003cnav\u003e\n            \u003ca [routerLink]=\"['/app1']\" routerLinkActive=\"active\"\u003e应用1\u003c/a\u003e\n            \u003ca [routerLink]=\"['/app2']\" routerLinkActive=\"active\"\u003e应用2\u003c/a\u003e\n        \u003c/nav\u003e\n        \u003crouter-outlet\u003e\u003c/router-outlet\u003e\n        \u003cdiv id=\"app-host-container\"\u003e\u003c/div\u003e\n        \u003cdiv *ngIf=\"!loadingDone\"\u003e加载中...\u003c/div\u003e\n    `\n})\nexport class AppComponent implements OnInit {\n    title = 'ngx-planet';\n\n    get loadingDone() {\n        return this.planet.loadingDone;\n    }\n\n    constructor(\n        private planet: Planet\n    ) {}\n\n    ngOnInit() {\n        this.planet.setOptions({\n            switchMode: SwitchModes.coexist,\n            errorHandler: error =\u003e {\n                console.error(`Failed to load resource, error:`, error);\n            }\n        });\n\n        this.planet.registerApps([\n            {\n                name: 'app1',\n                hostParent: '#app-host-container',\n                hostClass: 'thy-layout',\n                routerPathPrefix: '/app1',\n                preload: true,\n                entry: \"/static/app2/index.html\"\n            },\n            {\n                name: 'app2',\n                hostParent: '#app-host-container',\n                hostClass: 'thy-layout',\n                routerPathPrefix: '/app2',\n                preload: true,\n                entry: {\n                  basePath: \"/static/app1/\"\n                  manifest: \"index.html\"\n                  scripts: [\n                    'main.js'\n                  ],\n                  styles: [\n                    'styles.css'\n                  ]\n                }\n            }\n        ]);\n\n        // start monitor route changes\n        // get apps to active by current path\n        // load static resources which contains javascript and css\n        // bootstrap angular sub app module and show it\n        this.planet.start();\n    }\n}\n```\n\n### 3. Sub App define how to bootstrap\n\nfor NgModule application:\n```ts\ndefineApplication('app1', {\n    template: `\u003capp1-root class=\"app1-root\"\u003e\u003c/app1-root\u003e`,\n    bootstrap: (portalApp: PlanetPortalApplication) =\u003e {\n        return platformBrowserDynamic([\n            {\n                provide: PlanetPortalApplication,\n                useValue: portalApp\n            },\n            {\n                provide: AppRootContext,\n                useValue: portalApp.data.appRootContext\n            }\n        ])\n            .bootstrapModule(AppModule)\n            .then(appModule =\u003e {\n                return appModule;\n            })\n            .catch(error =\u003e {\n                console.error(error);\n                return null;\n            });\n    }\n});\n```\n\nfor Standalone application: (\u003e= 17.0.0)\n\n```ts\ndefineApplication('standalone-app', {\n    template: `\u003cstandalone-app-root\u003e\u003c/standalone-app-root\u003e`,\n    bootstrap: (portalApp: PlanetPortalApplication) =\u003e {\n        return bootstrapApplication(AppRootComponent, {\n            providers: [\n                {\n                    provide: PlanetPortalApplication,\n                    useValue: portalApp\n                },\n                {\n                    provide: AppRootContext,\n                    useValue: portalApp.data.appRootContext\n                }\n            ]\n        }).catch(error =\u003e {\n            console.error(error);\n            return null;\n        });\n    }\n});\n```\n\n## Documents\n\n### Sub app configurations\n\n| Name  | Type  | Description   | 中文描述   |\n| ------- | -------- | ----------| -------------- |\n| name      | string  | Application's name   | 子应用的名字  |\n| routerPathPrefix   | string   | Application route path prefix | 子应用路由路径前缀，根据这个匹配应用 |\n| selector           | string                | selector of app root component  | 子应用的启动组件选择器，因为子应用是主应用动态加载的，所以主应用需要先创建这个选择器节点，再启动 AppModule  |\n| entry    | string \\| PlanetApplicationEntry   | entry for micro app, contains manifest, scripts, styles | 入口配置，如果是字符串表示应用入口 index.html，如果是对象, manifest 为入口 html 或者 json 文件地址，scripts 和 styles 为指定的资源列表，未指定使用 manifest 接口中返回的所有资源，basePath 为基本路由，所有的资源请求地址前会带上 basePath  |\n| manifest    | string   | manifest json file path `deprecated` please use entry | manifest.json 文件路径地址，当设置了路径后会先加载这个文件，然后根据 scripts 和 styles 文件名去找到匹配的文件，因为生产环境的静态资文件是 hash 之后的命名，需要动态获取 |\n| scripts     | string[]     | javascript static resource paths `deprecated` please use entry.scripts  | JS 静态资源文件访问地址  |\n| styles      | string[]     | style static resource paths `deprecated` please use entry.styles  | 样式静态资源文件访问地址  |\n| resourcePathPrefix | string | path prefix of scripts and styles `deprecated` please use entry.basePath | 脚本和样式文件路径前缀，多个脚本可以避免重复写同样的前缀  |\n| hostParent | string or HTMLElement | parent element for render | 应用渲染的容器元素, 指定子应用显示在哪个元素内部 |\n| hostClass | string | added class for host which is selector | 宿主元素的 Class，也就是在子应用启动组件上追加的样式 |\n| switchMode   | default or coexist  | it will be destroyed when set to default, it only hide app when set to coexist | 切换子应用的模式，默认切换会销毁，设置 coexist 后只会隐藏 |\n| preload  | boolean   | start preload or not  | 是否启用预加载，启动后刷新页面等当前页面的应用渲染完毕后预加载子应用 |\n| loadSerial   | boolean | serial load scripts                                                            | 是否串行加载脚本静态资源  |\n\n### Communication between applications use GlobalEventDispatcher\n\n```\nimport { GlobalEventDispatcher } from \"@worktile/planet\";\n\n// app1 root module\nexport class AppModule {\n    constructor(private globalEventDispatcher: GlobalEventDispatcher) {\n        this.globalEventDispatcher.register('open-a-detail').subscribe(event =\u003e {\n            // dialog.open(App1DetailComponent);\n        });\n    }\n}\n\n// in other apps\nexport class OneComponent {\n    constructor(private globalEventDispatcher: GlobalEventDispatcher) {\n    }\n\n    openDetail() {\n        this.globalEventDispatcher.dispatch('open-a-detail', payload);\n    }\n}\n```\n\n### Cross application component rendering\n\n```\nimport { PlanetComponentLoader } from \"@worktile/planet\";\n\n// in app1\nexport class AppModule {\n    constructor(private planetComponentLoader: PlanetComponentLoader) {\n        this.planetComponentLoader.register([App1ProjectListComponent]);\n    }\n}\n```\n\nLoad `app1-project-list` (selector) component of app1 in other app via `PlanetComponentOutlet`\n\n```html\n\u003cng-container *planetComponentOutlet=\"'app1-project-list'; app: 'app1'; initialState: { search: 'xxx' }\"\u003e\u003c/ng-container\u003e\n\n// or \n\u003cng-container planetComponentOutlet=\"app1-project-list\"\n              planetComponentOutletApp=\"app1\"\n              [planetComponentOutletInitialState]=\"{ term: 'xxx' }\"\n              (planetComponentLoaded)=\"planetComponentLoaded($event)\"\u003e\n\u003c/ng-container\u003e\n```\n\nLoad `app1-project-list` component of app1 in other app via `PlanetComponentLoader`, must be call `dispose`\n\n```ts\n@Component({\n  ...\n})\nexport class OneComponent {\n    private componentRef: PlanetComponentRef;\n\n    constructor(private planetComponentLoader: PlanetComponentLoader) {\n    }\n\n    openDetail() {\n        this.planetComponentLoader.load('app1', 'app1-project-list', {\n            container: this.containerElementRef,\n            initialState: {}\n        }).subscribe((componentRef) =\u003e { \n            this.componentRef = componentRef;\n        });\n    }\n\n    ngOnDestroy() {\n       this.componentRef?.dispose();\n    }\n}\n```\n\n## FAQ\n\n### infinite loop load portal app's js\nBecause the portal app and sub app are packaged through webpack, there will be conflicts in module dependent files, we should set up additional config `runtimeChunk` through `@angular-builders/custom-webpack`, we expect webpack 5 to support micro frontend better.\n```\n// extra-webpack.config.js\n{    \n    optimization: {\n        runtimeChunk: false\n    }\n};\n```\n\n### throw error `Cannot read property 'call' of undefined at __webpack_require__ (bootstrap:79)`\nSimilar to the reasons above, we should set `vendorChunk` as `false` for `build` and `serve` in `angular.json`\n\n```\n ...\n \"build\": {\n    \"builder\": \"@angular-builders/custom-webpack:browser\",\n    \"options\": {\n          \"customWebpackConfig\": {\n              \"path\": \"./examples/app2/extra-webpack.config.js\",\n              \"mergeStrategies\": {\n                \"module.rules\": \"prepend\"\n              },\n              \"replaceDuplicatePlugins\": true\n          },\n          ...\n          \"vendorChunk\": false,\n          ...\n      },\n  },\n  \"serve\": {\n      \"builder\": \"@angular-builders/custom-webpack:dev-server\",\n      \"options\": {\n          ...\n          \"vendorChunk\": false\n          ...\n      }\n  }\n...\n```\n\n### throw error `An accessor cannot be declared in an ambient context.`\nthis is TypeScript's issue, details see [an-accessor-cannot-be-declared](https://stackoverflow.com/questions/61248058/error-ngx-daterangepicker-material-an-accessor-cannot-be-declared-in-an-ambient\n)\nshould setting `skipLibCheck` as true \n```\n\"compilerOptions\": {\n    \"skipLibCheck\": true\n}\n```\n\n### Production env throw error `Cannot read property 'call' of undefined` use router lazy load\n\nIn webpack 4 multiple webpack runtimes could conflict on the same HTML page, because they use the same global variable for chunk loading. To fix that it was needed to provide a custom name to the output.jsonpFunction configuration, details see [Automatic unique naming](https://webpack.js.org/blog/2020-10-10-webpack-5-release/#automatic-unique-naming).\n\nyou should set a unique name for each sub application in `extra-webpack.config.js`\n```\noutput: { jsonpFunction: \"app1\" }\n```\n\n## Development\n\n```\nnpm run start // open http://localhost:3000\n\nor\n\nnpm run serve:portal // 3000\nnpm run serve:app1 // 3001\nnpm run serve:app2 // 3002\n\n// test\nnpm run test\n```\n\n## Roadmap\n\n-   [ ] Ivy render engine\n-   [ ] Supports Other frameworks as React and Vue\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://www.zhihu.com/people/why520crazy/activities\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/3959960?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ewhy520crazy\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#question-why520crazy\" title=\"Answering Questions\"\u003e💬\u003c/a\u003e \u003ca href=\"#business-why520crazy\" title=\"Business development\"\u003e💼\u003c/a\u003e \u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=why520crazy\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#design-why520crazy\" title=\"Design\"\u003e🎨\u003c/a\u003e \u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=why520crazy\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#eventOrganizing-why520crazy\" title=\"Event Organizing\"\u003e📋\u003c/a\u003e \u003ca href=\"#infra-why520crazy\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e \u003ca href=\"#maintenance-why520crazy\" title=\"Maintenance\"\u003e🚧\u003c/a\u003e \u003ca href=\"#projectManagement-why520crazy\" title=\"Project Management\"\u003e📆\u003c/a\u003e \u003ca href=\"https://github.com/worktile/ngx-planet/pulls?q=is%3Apr+reviewed-by%3Awhy520crazy\" title=\"Reviewed Pull Requests\"\u003e👀\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/walkerkay\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/15701592?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eWalker\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=walkerkay\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#example-walkerkay\" title=\"Examples\"\u003e💡\u003c/a\u003e \u003ca href=\"#maintenance-walkerkay\" title=\"Maintenance\"\u003e🚧\u003c/a\u003e \u003ca href=\"https://github.com/worktile/ngx-planet/pulls?q=is%3Apr+reviewed-by%3Awalkerkay\" title=\"Reviewed Pull Requests\"\u003e👀\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://whyour.cn\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/22700758?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ewhyour\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=whyour\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://www.231jx.cn\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/19969080?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003e张威\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=aoilti\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/luxiaobei\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/13583957?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eluxiaobei\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#infra-luxiaobei\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e \u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=luxiaobei\" title=\"Tests\"\u003e⚠️\u003c/a\u003e \u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=luxiaobei\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/mario56\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/7720722?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003emario_ma\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/worktile/ngx-planet/commits?author=mario56\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-enable --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n\n## LICENSE\n\n[MIT License](https://github.com/worktile/ngx-planet/blob/master/LICENSE)\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworktile%2Fngx-planet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fworktile%2Fngx-planet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworktile%2Fngx-planet/lists"}