Giter VIP home page Giter VIP logo

ngx-planet's Introduction

ngx-planet

CircleCI Coverage Status npm (scoped) npm npm bundle size (scoped) All Contributors

A powerful, reliable, fully-featured and production ready Micro Frontend library for Angular.

APIs consistent with angular style, currently only supports Angular, other frameworks are not supported.

English | 中文文档

✨ Features

  • Rendering multiple applications at the same time
  • Support two mode, coexist and default that switch to another app and destroy active apps
  • Support application preload
  • Support style isolation
  • Built-in communication between multiple applications
  • Cross application component rendering
  • Comprehensive examples include routing configuration, lazy loading and all features

📖 Documentation

Alternatives

  • single-spa: A javascript front-end framework supports any frameworks.
  • 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.

Installation

$ npm i @worktile/planet --save
// or
$ yarn add @worktile/planet

Demo

Try out our live demo

ngx-planet-micro-front-end.gif

Usage

1. Loading NgxPlanetModule in the portal's AppModule

import { NgxPlanetModule } from '@worktile/planet';

@NgModule({
  imports: [
    CommonModule,
    NgxPlanetModule
  ]
})
class AppModule {}

2. Register applications to planet use PlanetService in portal app

@Component({
    selector: 'app-portal-root',
    template: `
        <nav>
            <a [routerLink]="['/app1']" routerLinkActive="active">应用1</a>
            <a [routerLink]="['/app2']" routerLinkActive="active">应用2</a>
        </nav>
        <router-outlet></router-outlet>
        <div id="app-host-container"></div>
        <div *ngIf="!loadingDone">加载中...</div>
    `
})
export class AppComponent implements OnInit {
    title = 'ngx-planet';

    get loadingDone() {
        return this.planet.loadingDone;
    }

    constructor(
        private planet: Planet
    ) {}

    ngOnInit() {
        this.planet.setOptions({
            switchMode: SwitchModes.coexist,
            errorHandler: error => {
                console.error(`Failed to load resource, error:`, error);
            }
        });

        this.planet.registerApps([
            {
                name: 'app1',
                hostParent: '#app-host-container',
                hostClass: 'thy-layout',
                routerPathPrefix: '/app1',
                preload: true,
                entry: "/static/app2/index.html"
            },
            {
                name: 'app2',
                hostParent: '#app-host-container',
                hostClass: 'thy-layout',
                routerPathPrefix: '/app2',
                preload: true,
                entry: {
                  basePath: "/static/app1/"
                  manifest: "index.html"
                  scripts: [
                    'main.js'
                  ],
                  styles: [
                    'styles.css'
                  ]
                }
            }
        ]);

        // start monitor route changes
        // get apps to active by current path
        // load static resources which contains javascript and css
        // bootstrap angular sub app module and show it
        this.planet.start();
    }
}

3. Sub App define how to bootstrap

for NgModule application:

defineApplication('app1', {
    template: `<app1-root class="app1-root"></app1-root>`,
    bootstrap: (portalApp: PlanetPortalApplication) => {
        return platformBrowserDynamic([
            {
                provide: PlanetPortalApplication,
                useValue: portalApp
            },
            {
                provide: AppRootContext,
                useValue: portalApp.data.appRootContext
            }
        ])
            .bootstrapModule(AppModule)
            .then(appModule => {
                return appModule;
            })
            .catch(error => {
                console.error(error);
                return null;
            });
    }
});

for Standalone application: (>= 17.0.0)

defineApplication('standalone-app', {
    template: `<standalone-app-root></standalone-app-root>`,
    bootstrap: (portalApp: PlanetPortalApplication) => {
        return bootstrapApplication(AppRootComponent, {
            providers: [
                {
                    provide: PlanetPortalApplication,
                    useValue: portalApp
                },
                {
                    provide: AppRootContext,
                    useValue: portalApp.data.appRootContext
                }
            ]
        }).catch(error => {
            console.error(error);
            return null;
        });
    }
});

Documents

Sub app configurations

Name Type Description 中文描述
name string Application's name 子应用的名字
routerPathPrefix string Application route path prefix 子应用路由路径前缀,根据这个匹配应用
selector string selector of app root component 子应用的启动组件选择器,因为子应用是主应用动态加载的,所以主应用需要先创建这个选择器节点,再启动 AppModule
entry string | PlanetApplicationEntry entry for micro app, contains manifest, scripts, styles 入口配置,如果是字符串表示应用入口 index.html,如果是对象, manifest 为入口 html 或者 json 文件地址,scripts 和 styles 为指定的资源列表,未指定使用 manifest 接口中返回的所有资源,basePath 为基本路由,所有的资源请求地址前会带上 basePath
manifest string manifest json file path deprecated please use entry manifest.json 文件路径地址,当设置了路径后会先加载这个文件,然后根据 scripts 和 styles 文件名去找到匹配的文件,因为生产环境的静态资文件是 hash 之后的命名,需要动态获取
scripts string[] javascript static resource paths deprecated please use entry.scripts JS 静态资源文件访问地址
styles string[] style static resource paths deprecated please use entry.styles 样式静态资源文件访问地址
resourcePathPrefix string path prefix of scripts and styles deprecated please use entry.basePath 脚本和样式文件路径前缀,多个脚本可以避免重复写同样的前缀
hostParent string or HTMLElement parent element for render 应用渲染的容器元素, 指定子应用显示在哪个元素内部
hostClass string added class for host which is selector 宿主元素的 Class,也就是在子应用启动组件上追加的样式
switchMode default or coexist it will be destroyed when set to default, it only hide app when set to coexist 切换子应用的模式,默认切换会销毁,设置 coexist 后只会隐藏
preload boolean start preload or not 是否启用预加载,启动后刷新页面等当前页面的应用渲染完毕后预加载子应用
loadSerial boolean serial load scripts 是否串行加载脚本静态资源

Communication between applications use GlobalEventDispatcher

import { GlobalEventDispatcher } from "@worktile/planet";

// app1 root module
export class AppModule {
    constructor(private globalEventDispatcher: GlobalEventDispatcher) {
        this.globalEventDispatcher.register('open-a-detail').subscribe(event => {
            // dialog.open(App1DetailComponent);
        });
    }
}

// in other apps
export class OneComponent {
    constructor(private globalEventDispatcher: GlobalEventDispatcher) {
    }

    openDetail() {
        this.globalEventDispatcher.dispatch('open-a-detail', payload);
    }
}

Cross application component rendering

import { PlanetComponentLoader } from "@worktile/planet";

// in app1
export class AppModule {
    constructor(private planetComponentLoader: PlanetComponentLoader) {
        this.planetComponentLoader.register([App1ProjectListComponent]);
    }
}

Load app1-project-list (selector) component of app1 in other app via PlanetComponentOutlet

<ng-container *planetComponentOutlet="'app1-project-list'; app: 'app1'; initialState: { search: 'xxx' }"></ng-container>

// or 
<ng-container planetComponentOutlet="app1-project-list"
              planetComponentOutletApp="app1"
              [planetComponentOutletInitialState]="{ term: 'xxx' }"
              (planetComponentLoaded)="planetComponentLoaded($event)">
</ng-container>

Load app1-project-list component of app1 in other app via PlanetComponentLoader, must be call dispose

@Component({
  ...
})
export class OneComponent {
    private componentRef: PlanetComponentRef;

    constructor(private planetComponentLoader: PlanetComponentLoader) {
    }

    openDetail() {
        this.planetComponentLoader.load('app1', 'app1-project-list', {
            container: this.containerElementRef,
            initialState: {}
        }).subscribe((componentRef) => { 
            this.componentRef = componentRef;
        });
    }

    ngOnDestroy() {
       this.componentRef?.dispose();
    }
}

FAQ

infinite loop load portal app's js

Because 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.

// extra-webpack.config.js
{    
    optimization: {
        runtimeChunk: false
    }
};

throw error Cannot read property 'call' of undefined at __webpack_require__ (bootstrap:79)

Similar to the reasons above, we should set vendorChunk as false for build and serve in angular.json

 ...
 "build": {
    "builder": "@angular-builders/custom-webpack:browser",
    "options": {
          "customWebpackConfig": {
              "path": "./examples/app2/extra-webpack.config.js",
              "mergeStrategies": {
                "module.rules": "prepend"
              },
              "replaceDuplicatePlugins": true
          },
          ...
          "vendorChunk": false,
          ...
      },
  },
  "serve": {
      "builder": "@angular-builders/custom-webpack:dev-server",
      "options": {
          ...
          "vendorChunk": false
          ...
      }
  }
...

throw error An accessor cannot be declared in an ambient context.

this is TypeScript's issue, details see an-accessor-cannot-be-declared should setting skipLibCheck as true

"compilerOptions": {
    "skipLibCheck": true
}

Production env throw error Cannot read property 'call' of undefined use router lazy load

In 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.

you should set a unique name for each sub application in extra-webpack.config.js

output: { jsonpFunction: "app1" }

Development

npm run start // open http://localhost:3000

or

npm run serve:portal // 3000
npm run serve:app1 // 3001
npm run serve:app2 // 3002

// test
npm run test

Roadmap

  • Ivy render engine
  • Supports Other frameworks as React and Vue

Contributors ✨

Thanks goes to these wonderful people (emoji key):


why520crazy

💬 💼 💻 🎨 📖 📋 🚇 🚧 📆 👀

Walker

💻 💡 🚧 👀

whyour

💻

张威

💻

luxiaobei

🚇 ⚠️ 💻

mario_ma

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

MIT License

ngx-planet's People

Contributors

allcontributors[bot] avatar aoilti avatar dependabot[bot] avatar handsomebutterball avatar lswl66 avatar luxiaobei avatar mario-mui avatar plrthink avatar pubuzhixing8 avatar walkerkay avatar why520crazy avatar whyour avatar zhenshuaiwws avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ngx-planet's Issues

请问单个应用部署时,如何访问应用下的路由?

例如我将app1编译打包放到容器中去了,这时候通过localhost:8080是能访问到这个应用的,但当我尝试访问localhost:8080/#/app1或者localhost:8080/#/app1/dashboard时,都不能加载对应的路由界面,是我没有正确访问,还是说本身并不支持?

可以加载vue或react的前端项目吗?

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

cant serve application after run build

Describe the bug
I can't serve this application after ran "npm run build", which listed in the [package.json] file

To Reproduce
Steps to reproduce the behavior:

  1. npm run build
  2. serve -s dist/ 《== There is no response
    although the below command can run a server successfully, but I can't reach app1 and app2 page
    2.serve -s dist/planet

Expected behavior
how can i serve a application successfully with this framework

Screenshots
image

Error occurred while trying to proxy request

Hi
I cloned project to the local end and run ng serve, without any modifications, but got this error

[HPM] Error occurred while trying to proxy request /static/app2/manifest.json?t=1582204653047 from localhost: 3000 to http: // localhost: 3002 (ECONNREFUSED) (https://nodejs.org/api/errors. html # errors_common_system_errors)
[HPM] Error occurred while trying to proxy request /static/app2/manifest.json?t=1582204656987 from localhost: 3000 to http: // localhost: 3002 (ECONNREFUSED)

I am wondering how to solve this problem.

Thanks!

Screenshots

image

My cli version
image

Uncaught Error: No NgModule metadata found for 'Ix'.

-----main.ts-----

if (window['planet'] && window['planet']['portalApplication']) {
  defineApplication('micro-test', (portalApp: PlanetPortalApplication) => {
    return platformBrowserDynamic([
      {
        provide: PlanetPortalApplication,
        useValue: portalApp
      }
    ])
      .bootstrapModule(AppModule)
      .then(appModule => {
        return appModule;
      })
      .catch(error => {
        console.error(error);
        return null;
      });
  });
} else {
  const bootstrap = () => {
    return platformBrowserDynamic()
      .bootstrapModule(AppModule)
      .then(res => {
        if ((window as any).appBootstrap) {
          (window as any).appBootstrap();
        }
        return res;
      });
  };
  bootstrap();
}

ng build success
open the url error: Uncaught Error: No NgModule metadata found for 'Ix'.
    at ey.resolve (main-es2015.d748b8093d9a2467be10.js:1)
    at Vv.getNgModuleMetadata (main-es2015.d748b8093d9a2467be10.js:1)
    at ky._loadModules (main-es2015.d748b8093d9a2467be10.js:1)
    at ky._compileModuleAndComponents (main-es2015.d748b8093d9a2467be10.js:1)
    at ky.compileModuleAsync (main-es2015.d748b8093d9a2467be10.js:1)
    at useClass.compileModuleAsync (main-es2015.d748b8093d9a2467be10.js:1)
    at main-es2015.d748b8093d9a2467be10.js:1
    at Object.bootstrapModule (main-es2015.d748b8093d9a2467be10.js:1)
    at Module.zUnb (main-es2015.d748b8093d9a2467be10.js:1)
    at n (main-es2015.d748b8093d9a2467be10.js:1)

Angular 7 应该使用哪个版本?

ngx-planet 看上去是个不错的微前端库。
尝试过程中遇到这样的问题:

angular7

这似乎是依赖版本 不对,请问,可以用ngx-planet 的某个版本开发angular 7的应用吗

Feature: upgrade angular to 8.x

目前的ngx-planet是基于angular7.x的,很多编译的插件也是强绑定的,变更版本后就需要大量修改,该框架有升级的计划吗

项目文件结构不能分开吗

Is your feature request related to a problem? Please describe.
请问,作者您实际开发环境就跟github里面的目录结构一样的吗?多个子应用都是位于同一个项目里的不同文件夹,有没有可能主应用,子应用都是单独的项目结构呢?请问您是否在实际开发环境中,使用ngx-planet的同时,子应用是单独的项目的?

Describe the solution you'd like
我目前遇到的实际情况是,由于引用到了ng-alain,而ng-alain在使用ng add 命令添加的时候做了很多操作,导致默认的ng cli 生成的目录啊,配置啊有改动,如果是手工同步那些改动到主子应用同一个目录的话,有点复杂,且容易出错,不好维护,所以想每个应用还是保持单独的几乎标准的angular项目结构

Examples support Lazy Loading route

使用懒加载路由模块会报错,报错信息如下:
core.js:15724 ERROR Error: Uncaught (in promise): TypeError: undefined is not a function
TypeError: undefined is not a function
at Array.map ()
at webpackAsyncContext ($_lazy_route_resource lazy namespace object:18)
at SystemJsNgModuleLoader.../../node_modules/@angular/core/fesm5/core.js.SystemJsNgModuleLoader.loadAndCompile (core.js:18359)
at SystemJsNgModuleLoader.../../node_modules/@angular/core/fesm5/core.js.SystemJsNgModuleLoader.load (core.js:18351)
at RouterConfigLoader.../../node_modules/@angular/router/fesm5/router.js.RouterConfigLoader.loadModuleFactory (router.js:3614)
at RouterConfigLoader.../../node_modules/@angular/router/fesm5/router.js.RouterConfigLoader.load (router.js:3602)
at MergeMapSubscriber.project (router.js:2605)
at MergeMapSubscriber.../../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._tryNext (mergeMap.js:61)
at MergeMapSubscriber.../../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._next (mergeMap.js:51)
at MergeMapSubscriber.../../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at resolvePromise (zone.js:831)
at resolvePromise (zone.js:788)
at zone.js:892
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)
at Object.onInvokeTask (core.js:17290)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:422)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195)
at drainMicroTaskQueue (zone.js:601)
at push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask (zone.js:502)
at ZoneTask.invoke (zone.js:487)
切换回子路由形式就不会报错了

Multiples errors using on Angular 8

Hi! First of all I want to say thanks you for your works! Looks great for spliting a huge modular app! I have questions about your library:

  • I started using your library on Angular v8.3.28 and in the root app I had imported the NgxPlanetModule and I have this error: "An accessor cannot be declared in an ambient context."

Screenshot from 2020-07-15 19-56-20

  • Is this library compatible with Angular 8? More details about my environment:
    Screenshot from 2020-07-15 20-03-12

  • Do you rercommend this library for a production environment?

添加新的子应用

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
我需要新增一个子应用

Describe the solution you'd like
A clear and concise description of what you want to happen.
按照app1和app2那样进行构建app3
不知道哪里出错了,没有构建成功
能否有一个详细构建例子?

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
或者能否有一个构建新的子应用的文章,谢谢。

Additional context
Add any other context or screenshots about the feature request here.
这是对planet修改,但是没有添加相关的xxxx.spec.ts的文件内容
image

这是对angular.json进行的修改,这里应该没任何问题
image

这里是按正常创建angular应用程序那样,应该也没问题
image

没有任何报错,也是页面不展示,可能是没写测试代码的关系。
image

部署问题

看了下portal和app1与app2都使用了package下的ngx-planet,那最后打包的时候,ngx-planet是单独打包单独部署吗,还是分别被打包到了三个应用中?

manifest.json 404

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.
image

Sub app be injected global service is an error

Hi Planet team

It's so sorry to bother you, but I'm confused by this library although these questions may not all come from it, and I have not found an answer. May i get some help.

I have a difficult case which was organized from an old angularjs and angular8, besides it's was also limited in the extensive refactoring to resolve in code of micronization.

Issue:

When I installed planet for it, it was up and running, I get this error message.
image

I have tested this way of writing under angularjs and angular8 code without planet library, it can work, but after using planet, the aforementioned error occurred.

After searching, it was found that the reasons for this error message are known as the following 6 types:

  1. Barrel index
  2. Circularity dependency
  3. Forgot to enable polyfills import'core-js/es6/reflect'
  4. Injectable decorator incorrect usage (EX: missing @ or capital & lower case error etc...)
  5. Tsconfig does not configure emitDecoratorMetadata
  6. Decorate parameter type use any in your constructor

The first 5 have been excluded, I suspect it is the last, because of this Configuring Dependency Injection in Angular
But I am confused, whether a certain configuration of planet causes parameter type to fail?

Code Structure:

1. There is a common service exported from angularjs

(File name: angular1-root-module.js)

(function () {

  angular.module('angular1', [
    'angular1.export-service'
  ]);

  angular.module('angular1.export-service', []);

  angular.module('angular1.export-service').factory('Angular1ExportService', Angular1ExportService);

  Angular1ExportService.$inject = [];

  function Angular1ExportService() {
    function outPutString() {
      return 'I from Angular1 export service string';
    }
    return {
      outPutAngular1String: outPutString,
    };
  }
})();

2. Inject into the class named Angular1InjectorService through the factory provider and depend on angularjs's $injector

export function Angular1InjectorServiceFactory(injector: any) {
  return new Angular1InjectorService(injector);
}

export const Angular1InjectorServiceProvider = {
  provide: Angular1InjectorService,
  useFactory: Angular1InjectorServiceFactory,
  deps: ['$injector']
};
@Injectable()
export class Angular1InjectorService {

  // I think probably this injector of type is any cause
  constructor(private angular1Injector: any) {
  }

  getService(serviceName: String) {
    return this.angular1Injector.get(serviceName);
  }
}

3. Then inject into the common AppBaseService

@Injectable()
export class AppBaseService {

  readonly angular1InjectorService: Angular1InjectorService;
  readonly testService: any;

  constructor(readonly injector: Injector) {
    this.angular1InjectorService = this.injector.get(Angular1InjectorService);
    this.testService = this.angular1InjectorService.getService('Angular1ExportService');
  }

  testGetAngular1String() {
    console.log('app base service is work!');
    return this.testService.outPutAngular1String();
  }
}

4. Then the service of the sub app inherits AppBaseService, and obtains the method that exists in angularjs

export class App1RootService extends AppBaseService {

  constructor(readonly injector: Injector) {
    super(injector);
  }

  GetLogAngular1String() {
    console.log('app1 root service is work!');
    return this.testGetAngular1String();
  }
}

5. portal root planet config

    this.planet.registerApps([
      {
        name: 'app1',
        hostParent: '#app-host-container',
        routerPathPrefix: '/app1',
        selector: 'app1-root-container',
        resourcePathPrefix: '/static/app1',
        preload: settings.app1.preload,
        switchMode: settings.app1.switchMode,
        loadSerial: true,
        manifest: '/static/app1/manifest.json',
        scripts: [
          'main.js'
        ],
        styles: [
        ],
      }
    ]);

6. sub app1 main config

defineApplication('app1', (portalApp: PlanetPortalApplication) => {
  return platformBrowserDynamic([
    {
      provide: PlanetPortalApplication,
      useValue: portalApp
    },
    {
      provide: AppRootContext,
      useValue: portalApp.data.appRootContext
    }
  ])
    .bootstrapModule(App1RootModule)
    .then(appModule => {
      return appModule;
    })
    .catch(error => {
      console.error(error);
      return null;
    });
});

Issue related resources:

EXCEPTION: Can't resolve all parameters
Uncaught Error: Can't resolve all parameters for
After upgrade Angular to v8: Can't resolve all parameters for Component:

Intact issue code:

Stackblitz
Github

hope you could give me solution, and thanks for your hospitable help

Sincerely planet user.

主应用和子应用同时使用angular cdk overlay时有冲突

主应用和子应用同时使用了ng-zorro-antd组件库,主应用上导航使用了ng-zorro-antd下拉菜单dropdown组件,发现只要加载了子应用,主应用上导航下拉菜单点击后没反应,之前也在调研angular element开发微前端也遇到类似问题,排查后发现子应用里使用的ng-zorro-antd组件导致主应用有问题的,这些组件都使用了cdk overlay.

查看single spa微前端在使用material组件库也有类似情况,
链接: angular/components#16972
这个应该和ngx-planet没关系,想咨询下作者之前有遇到过这个问题吗?

angular version:  "@angular/core": "^8.2.14",
angular cdk version:  "@angular/cdk": "^8.1.3",
ng-zorro-antd version:   "ng-zorro-antd": "^8.5.2",

The selector "**" did not match any elements

用 ng serve 运行时, 子应用报错,虽然子应用内容显示正常

  1. 注册应用

this.planet.registerApps([
{
name: 'weixin',
hostParent: '#app-host-container',
routerPathPrefix: '/weixin',
selector: 'weixin-root',
resourcePathPrefix: '/static/weixin/',
preload: false,
scripts: [
'main.js'
],
},
// start monitor route changes
// get apps to active by current path
// load static resources which contains javascript and css
// bootstrap angular sub app module and show it
this.planet.start();
}


  1. 子应用main.ts
defineApplication('weixin', (portalApp: PlanetPortalApplication) => {
    return platformBrowserDynamic([
        {
            provide: PlanetPortalApplication,
            useValue: portalApp
        }
    ])
        .bootstrapModule(AppModule)
        .then(appModule => {
            return appModule;
        })
        .catch(error => {
            console.error(error);
            return null;
        });
});
  1. 子应用 AppModule
...
const routes: Routes = [
  {path: 'weixin', component: AppActualRootComponent},
]
...
bootstrap: [AppRootComponent],
...
  1. 子应用 root.component.ts
....
@Component({
  selector: 'weixin-root-actual',
  templateUrl: './root.component.html',
  styleUrls: ['./root.component.css']
[})]([url](url))
export class AppActualRootComponent implements OnInit, OnDestroy {
...
@Component({
  selector: 'weixin-root', // tslint:disable-line
  template: '<router-outlet></router-outlet>'
})
export class AppRootComponent  {
}
...
  1. 检查元素,有发现相应标签
    image

  2. 报错的原因应该是说找不到 weixin-root 标签,报错具体代码如下

selectRootElement(selectorOrNode, preserveContent) {
        /** @type {?} */
        let el = typeof selectorOrNode === 'string' ? document.querySelector(selectorOrNode) :
            selectorOrNode;
        if (!el) {
            throw new Error(`The selector "${selectorOrNode}" did not match any elements`);
        }
        if (!preserveContent) {
            el.textContent = '';
        }
        return el;
    }

想知道怎么消除这个报错,请哪位大牛帮我看下,非常感谢 ^^

子应用刷新后会出现加载错误信息

@why520crazy @walkerkay
当子应用路由激活状态下,刷新页面会抛异常,找不到当前应用,但是重新从主应用再进一次就好了。错误如图所示
QQ截图20201113154520
package json如下
"dependencies": { "@angular/animations": "~9.1.12", "@angular/cdk": "^9.2.4", "@angular/common": "~9.1.12", "@angular/compiler": "~9.1.12", "@angular/core": "~9.1.12", "@angular/forms": "~9.1.12", "@angular/platform-browser": "~9.1.12", "@angular/platform-browser-dynamic": "~9.1.12", "@angular/router": "~9.1.12", "ngx-sortablejs": "^3.1.4", "ngx-bootstrap": "5.2.0", "ngx-tethys": "8.1.0", "rxjs": "~6.5.5", "sortablejs": "1.8.4", "tslib": "^1.10.0", "zone.js": "~0.10.2", "bootstrap": "4.3.1", "ng-zorro-antd": "9.3.0" },

When the hashlocationstrategy is turned on, when switching from portal to subapp root, the hash of url will disappear

Describe

I am transforming an old project. When dealing with routing, it all uses hashlocationstrategy. I configure the routing according to the planet example, I find when hashlocationstrategy is turned on and route is switched from portal to sub app root, the hash of url will disappear , But when entering the lower page route of sub app, the hash is displayed normally, I don't know what's wrong

To Reproduce

  1. Portal to sub app root
  2. Sub app root to sub app page

Expected behavior

When use hashlocationstrategy routing # always exist.

Screenshots

  1. Routing in the portal home
    image

  2. Routing to sub app1 root
    image

  3. Routing to app1 belong to dashboard page
    image

Package json

{
  "name": "",
  "version": "",
  "scripts": {
    ... Omit
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "8.2.0",
    "@angular/cdk": "8.2.3",
    "@angular/common": "8.2.0",
    "@angular/compiler": "8.2.0",
    "@angular/core": "8.2.0",
    "@angular/forms": "8.2.0",
    "@angular/http": "7.2.15",
    "@angular/platform-browser": "8.2.0",
    "@angular/platform-browser-dynamic": "8.2.0",
    "@angular/router": "8.2.0",
    "@angular/upgrade": "8.2.0",
    "@worktile/planet": "1.1.2",
    "lodash": "4.17.15",
    "rxjs": "6.5.3",
    "zone.js": "0.9.1"
  },
  "devDependencies": {
    "@angular-builders/custom-webpack": "8.4.1",
    "@angular-builders/dev-server": "7.3.1",
    "@angular-devkit/build-angular": "0.802.2",
    "@angular-devkit/build-ng-packagr": "0.803.20",
    "@angular/cli": "8.2.0",
    "@angular/compiler-cli": "8.2.0",
    "@angular/language-service": "6.0.2",
    "@types/jasmine": "2.5.45",
    "@types/jasminewd2": "2.0.3",
    "@types/node": "6.0.60",
    "codelyzer": "5.0.1",
    "concurrently": "4.1.0",
    "jasmine-core": "2.6.4",
    "jasmine-spec-reporter": "4.1.1",
    "karma": "4.4.1",
    "karma-chrome-launcher": "3.1.0",
    "karma-coverage-istanbul-reporter": "2.1.1",
    "karma-jasmine": "3.1.0",
    "karma-jasmine-html-reporter": "1.5.1",
    "protractor": "5.3.0",
    "ts-node": "5.0.1",
    "tslint": "5.9.1",
    "typescript": "3.5.3",
    "webpack-assets-manifest": "3.1.1"
  }
}

Portal routing module

... Omit import 

const routes: Routes = [
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  },
  {
    path: 'home',
    component: HomeComponent
  },
  {
    path: 'app1',
    component: EmptyComponent,
    children: [
      {
        path: '**',
        component: EmptyComponent
      }
    ]
  },
  {
    path: 'app2',
    component: EmptyComponent,
    children: [
      {
        path: '**',
        component: EmptyComponent
      }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes,
    {
      paramsInheritanceStrategy: 'always',
      useHash: true // I just open this option additionally
    }
  )],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

Test: improve test coverage for AssetsLoader

Is your feature request related to a problem? Please describe.
The AssetsLoader include all utils methods to load assets(scripts, styles and manifest), currently, there is no test cases for loadStyles and loadScriptsAndStyles, and the test coverage of assets-loader file is 49% as:
https://coveralls.io/builds/27793312/source?filename=packages%2Fplanet%2Fsrc%2Fassets-loader.ts

Describe alternatives you've considered
Need add test cases for loadStyles and loadScriptsAndStyles , expect improve to test coverage 100%

module 名称相同

app1 和 app2 项目下,都有一个UserModule,导致两个项目只请求一个UserModule(可能是缓存),这种情况怎么解决
image

Renders two apps at the same time on host container when just requested one

Describe the bug
The first time load the respective subapps but the second time, load two apps (previus and new app) at the same time. And the third time, you request the previus app, loads correctly

To Reproduce
Steps to reproduce the behavior:

  1. Load the app
  2. Click on new app (app 1)
  3. Click on second app new app (app 2)
  4. See error (app 1 and app 2 loaded)
  5. Click on the previos app (app1) and loads correctly

Expected behavior
Load only the request app according the path in routerPathPrefix in registerApps of Planet

Screenshots

  • Views and paths

01BugVista

  • Check styles

02BugHTMLElements

Desktop:

  • OS: Linux Ubuntu 20
  • Browser Chrome
  • Version 84.0.4147.89 (Official Build) (64-bit)

Smartphone(Not tested):

  • Device: N/A
  • OS: N/A
  • Browser N/A
  • Version N/A

Additional context

It looks like it's about styles or something. Check second gif

global loadingdone should not reset false when router changes every times

Describe the bug
I use global loadingDone in portal apps to control loading difference app, but when i switch router in native apps, this loadingDone reset false. then my app appears transient loading, although the loading time very short .

To Reproduce
Steps to reproduce the behavior:

  1. go to an app
  2. switch others router, example detail or other modules

Expected behavior
will appear transient loading. then disappear.

Screenshots

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Feature: simplify subapplication's definition

Now, we define a subapplication as:

defineApplication('app1', (portalApp: PlanetPortalApplication) => {
    return platformBrowserDynamic([
        {
            provide: PlanetPortalApplication,
            useValue: portalApp
        }
    ])
        .bootstrapModule(AppModule)
        .then(appModule => {
            return appModule;
        })
        .catch(error => {
            console.error(error);
            return null;
        });
});

Expect define as:

defineApplication('app1', {
   platformRef: platformBrowserDynamic(),
   AppModule: AppModule,
   // or 
   bootstrap:(options) => {
     return platformBrowserDynamic()
        .bootstrapModule(AppModule)
        .then(appModule => {
            return appModule;
        })
   }
   template: `<app1-root></app1-root>`
   Router: Router,
  // ... and somethings
});

at present, it is not appropriate to determine how to define API, look forward to your good suggestions.

The single-spa for Angular as below:
https://github.com/CanopyTax/single-spa-angular/blob/master/src/browser-lib/single-spa-angular.ts

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.