You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Single SPA中实现Angular与Angular-parcel多组件条件化调用

实现Angular应用动态切换Single SPA Parcel中的组件

我已搭建两个应用:angular-parcel应用与Angular应用,并将二者均挂载至Single SPA根应用中。随后通过single-spa将angular-parcel应用挂载到Angular应用内,现有实现代码如下:

angular-parcel 的 main.single-spa.ts 文件

import { enableProdMode, NgZone } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { singleSpaAngular } from 'single-spa-angular';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { singleSpaPropsSubject } from './single-spa/single-spa-props';
import { ParcelModuleModule } from './app/parcel-module/parcel-module.module';

if (environment.production) {
  enableProdMode();
}

const lifecycles = singleSpaAngular({
  bootstrapFunction: (singleSpaProps) => {
    return platformBrowserDynamic().bootstrapModule(AppModule);
  },
  template: '<app-parcel />',
  NgZone,
});

export const name = 'angular-parcel';
export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;

Angular应用的 app.component.ts 文件

import { Component } from '@angular/core';
import { mountRootParcel } from 'single-spa';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent  {
  title = 'angular-app';
  mountRootParcel = mountRootParcel;
  parcelProps = { customProp1: 'Parent prop1' };
  target = document.body;

  async ngconfig() {
    return (window as any).System.import('@dist/angular-parcel');
  }

  ngparcelMounted():void {
    console.log('Angular parcel mounted');
  }
}

Angular应用的 app.component.html 文件

<parcel
  [config]="ngconfig"
  [mountParcel]="mountRootParcel"
  [customProps]="parcelProps"
  wrapWith="h1"
  [onParcelMount]="ngparcelMounted"
  [appendTo]="target"
  domElement="app-sub-parcel"
></parcel>

Angular应用的 app.module.ts 文件

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { ParcelComponent } from 'single-spa-angular/parcel';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, ParcelComponent],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

当前需求:angular-parcel应用的AppModule中包含多个组件,但目前默认仅展示第一个组件,希望能在Angular应用中根据条件调用其他组件。


解决方案

步骤1:修改angular-parcel应用,支持根据传入参数动态切换组件

  1. 传递Single SPA Props到应用内部
    修改main.single-spa.ts中的bootstrapFunction,将父应用传入的props传递给singleSpaPropsSubject,让应用内部可以监听props变化:

    bootstrapFunction: (singleSpaProps) => {
      // 新增:将props传入subject,供内部组件订阅
      singleSpaPropsSubject.next(singleSpaProps);
      return platformBrowserDynamic().bootstrapModule(AppModule);
    },
    
  2. 在angular-parcel根组件中实现动态渲染逻辑
    修改angular-parcel的app-parcel.component.ts,通过订阅props切换显示的组件:

    import { Component, OnInit } from '@angular/core';
    import { singleSpaPropsSubject } from '../single-spa/single-spa-props';
    
    @Component({
      selector: 'app-parcel',
      template: `
        <!-- 根据父组件传入的targetComponent值渲染对应组件 -->
        <app-component1 *ngIf="currentComponent === 'component1'"></app-component1>
        <app-component2 *ngIf="currentComponent === 'component2'"></app-component2>
        <app-component3 *ngIf="currentComponent === 'component3'"></app-component3>
      `
    })
    export class AppParcelComponent implements OnInit {
      // 默认显示第一个组件
      currentComponent = 'component1';
    
      ngOnInit() {
        // 订阅props变化,更新当前要显示的组件
        singleSpaPropsSubject.subscribe(props => {
          if (props.customProps?.targetComponent) {
            this.currentComponent = props.customProps.targetComponent;
          }
        });
      }
    }
    

    注意:确保所有需要切换的组件都已在AppModule的declarations中声明,或导入了对应的子模块。

步骤2:修改Angular父应用,传递控制参数并支持动态更新

  1. 更新父组件逻辑,添加组件切换控制
    修改Angular应用的app.component.ts,添加组件切换方法并保存parcel实例用于更新:

    import { Component } from '@angular/core';
    import { mountRootParcel } from 'single-spa';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    export class AppComponent  {
      title = 'angular-app';
      mountRootParcel = mountRootParcel;
      // 新增targetComponent字段,控制parcel显示哪个组件
      parcelProps = { 
        customProp1: 'Parent prop1',
        targetComponent: 'component1'
      };
      target = document.body;
      // 保存parcel实例,用于后续更新props
      parcelInstance: any;
    
      async ngconfig() {
        return (window as any).System.import('@dist/angular-parcel');
      }
    
      // 更新挂载回调,保存parcel实例
      onParcelMounted(parcel: any) {
        this.parcelInstance = parcel;
        console.log('Angular parcel mounted');
      }
    
      // 切换组件并通知parcel更新
      switchComponent(componentKey: string) {
        this.parcelProps.targetComponent = componentKey;
        // 调用parcel的update方法传递新的props
        if (this.parcelInstance) {
          this.parcelInstance.update(this.parcelProps);
        }
      }
    }
    
  2. 更新父组件模板,添加切换控件
    修改Angular应用的app.component.html,添加按钮或其他控件触发组件切换(可根据业务需求替换为下拉框、条件判断等触发方式):

    <button (click)="switchComponent('component1')">显示组件1</button>
    <button (click)="switchComponent('component2')">显示组件2</button>
    <button (click)="switchComponent('component3')">显示组件3</button>
    
    <parcel
      [config]="ngconfig"
      [mountParcel]="mountRootParcel"
      [customProps]="parcelProps"
      wrapWith="div"
      [onParcelMount]="onParcelMounted"
      [appendTo]="target"
      domElement="app-sub-parcel"
    ></parcel>
    

内容的提问来源于stack exchange,提问作者sahil verma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:20:41