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