Angular 12 Module Federation, host app loadRemoteModule error Cannot read properties of null (reading ‘bindingStartIndex’)

I am working on Angular 12 with webpack federation

I created a micro header app and trying to load the header component into another app using loadRemoteModule. I have been playing with the webpack.config.js to figure how to get the header to load on the host app but been unsuccessful. I am getting this error message on Angular

‘Uncaught (in promise): TypeError: Cannot read properties of null (reading ‘bindingStartIndex’)nTypeError: Cannot read properties of null (reading ‘bindingStartIndex’)n at Module.ɵɵelementStart (http://localhost:4201/remoteEntry .js:20977:46)n at HeaderMFEComponent_Template (http://localhost:4201/src_app_header-app_src_app_header_header-mfe_component_ts.js:42:69)n at executeTemplate (http://localhost:4200/180:vendor. :9)n at renderView (http://localhost:4200/vendor.js:17883:13)n at renderComponent (http://localhost:4200/vendor.js:19164:5)n at renderChildComponents ( http://localhost:4200/vendor.js:17748:9)n at renderView (http://localhost:4200/vendor.js:17908:13)n at ComponentFactory$1.create (http://localhost :4200/vendor.js:33618:13)n at ViewContainerRef.createComponent (http://localhost:4200/vendor.js:31653:47)n at http://localhost:4200/src_bootstrap_ts.js:69650 :40′

I see alot of these Cannot read undefined here but they are all different.

From the header App, my webpack.config.js is the following

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;

const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
  path.join(__dirname, '../../../tsconfig.json'),
  [/* mapped paths to share */]);

module.exports = {
  output: {
    uniqueName: "headerApp",
    publicPath: "auto",
    scriptType: 'text/javascript'
  },
  optimization: {
    runtimeChunk: false
  },   
  resolve: {
    alias: {
      ...sharedMappings.getAliases(),
    }
  },
  experiments: {
    outputModule: true
  },
  plugins: [
    new ModuleFederationPlugin({
        //library: { type: "module" }, 
        name: "header",
        filename: "remoteEntry.js",
        exposes: {
            './HeaderComponent': 'src/app/header-app/src/app/header/header-mfe.component.ts',
        },      
 
        shared: share({
          "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true }, 
          "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true }, 
          "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true }, 
          "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true },

          ...sharedMappings.getDescriptors()
        })
        
    }),
    sharedMappings.getPlugin()
  ],
};

To the host app to read the http://localhost:4201/remoteEntry.js

I had to comment out the library type: module and add the eager: true to my shared libraries. If I did not, the loadRemoteModule on the host App will throw an error.

On the host app, the webpack.config.js is

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;

const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
  path.join(__dirname, 'tsconfig.json'),
  [/* mapped paths to share */]);

module.exports = {
  output: {
    uniqueName: "home",
    publicPath: "auto",
    scriptType: 'text/javascript'
  },
  optimization: {
    runtimeChunk: false
  },   
  resolve: {
    alias: {
      ...sharedMappings.getAliases(),
    }
  },
  experiments: {
    outputModule: true
  },
  plugins: [
    new ModuleFederationPlugin({
        //library: { type: "module" },
 
        // For hosts (please adjust)
        remotes: {
            "headerApp": "http://localhost:4201/remoteEntry.js",
        },

        shared: share({
          "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true }, 
          "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true }, 
          "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true }, 
          "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true },

          ...sharedMappings.getDescriptors()
        })
        
    }),
    sharedMappings.getPlugin()
  ],
};

I also been modifying the tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2020",
    "module": "esnext",
    "lib": [
      "es2020",
      "dom"
    ],
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "baseUrl": ".",
    "paths": {}
  },
  "exclude": [
    "node_modules",
    "tmp"
  ]
}

This is where the host app code is throwing an error

export class HeaderMFEComponent implements OnInit {

  constructor(
    private cfr: ComponentFactoryResolver,
    private vcref: ViewContainerRef
  ) { }

  async ngOnInit() {

    let remote = await loadRemoteModule({
      remoteEntry: 'http://localhost:4201/remoteEntry.js',
      remoteName: 'header',
      exposedModule: './HeaderComponent',
    });

    const ComponentRef: ComponentRef<{
      header: string;
    }> = this.vcref.createComponent(
      this.cfr.resolveComponentFactory(remote.HeaderMFEComponent)
    );
    ComponentRef.instance.header="p";
  }

}

When I stepped thru the code here, the remote.HeaderMFEComponent has the ecmp populated object. I can see the styling classes in the ecmp. However, when I get to the resolveComponentFactory that is where the error is thrown.

I am not sure what is bindingStartIndex and could not find it in the remoteEntry.js

The error message doesn’t give alot of clues on what is missing.

Thanks in advance

Leave a Comment