single-spa-angular
single-spa-angular copied to clipboard
Angular 11 Shared Library - NullInjectorError: No provider for N_!
I am trying to get shared angular libraries to work but get this stack trace:
NullInjectorError: No provider for N_!
at Ra.get (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:101406)
at https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:109184
at lc (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:109556)
at rc.get (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:107393)
at W_ (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:243001)
at ew.bootstrapModule (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:245315)
at Object.bootstrapFunction (http://localhost:4200/main.js:1904:179)
at http://localhost:4200/main.js:5793:42
at Generator.next (<anonymous>)
at http://localhost:4200/main.js:5421:71
My import map looks like:
<script type="systemjs-importmap">
{
"imports": {
"@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js",
"@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js",
"@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js",
"@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js",
"rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs.min.js",
"rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs-operators.min.js",
"single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
"single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.min.js",
"@cxone/app1": "http://localhost:4200/main.js"
}
}
</script>
Root config:
import { registerApplication, start } from "single-spa";
registerApplication({
name: "app1",
app: () =>
System.import(
"@cxone/app1"
),
// activeWhen: () => true,
activeWhen: ["/app1"]
});
registerApplication({
name: "app2",
app: () =>
System.import(
"http://localhost:4201/main.js"
),
activeWhen: ["/app2"],
});
start({
urlRerouteOnly: true,
});
My web app extra-webpack-config.js
const singleSpaAngularWebpack = require('single-spa-angular/lib/webpack').default;
const webpackMerge = require("webpack-merge");
const SystemJSPublicPathWebpackPlugin = require("systemjs-webpack-interop/SystemJSPublicPathWebpackPlugin");
module.exports = (config, options) => {
const singleSpaWebpackConfig = singleSpaAngularWebpack(config, options);
singleSpaWebpackConfig.plugins.push(new SystemJSPublicPathWebpackPlugin({
// ONLY NEEDED FOR WEBPACK 1-4. Not necessary for webpack@5
systemjsModuleName: "@cxone/app1"
}));
singleSpaWebpackConfig.externals.push(
"rxjs",
"rxjs/operators",
/^@angular\/.*/,
);
return singleSpaWebpackConfig;
};
This is what my network requests look like

I have this exact same problem. Everything works fine if Angular is packed with the app, but after moving to shared system.js import, the bootstrap function fails.
Based on my network traffic screenshot above. main.js is retrieved before any of the angular libraries. I wonder if that is part of the issue.
I'm also unable to get a working example up and running with shared angular dependency due to this issue.
I don't spend much time on single-spa-angular anymore, since I don't know or enjoy angular very much. If you can provide a repository demonstrating the issue, @arturovt may be able to investigate this.
I think I made some progress. I made sure 'single-spa' and 'single-spa-angular' were in my child's app's webpack externals config. However, I now get this error: Unable to resolve bare specifier 'single-spa-angular/internals' .
I do have single-spa and single-spa-angular in my import map:
<script type="systemjs-importmap">
{
"imports": {
"@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js",
"@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js",
"@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js",
"@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js",
"rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs.min.js",
"rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs-operators.min.js",
"single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
"single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.min.js",
"@cxone/app1": "http://localhost:4200/main.js",
"@cxone/app2": "http://localhost:4202/main.js"
}
}
</script>
Could anyone of you guys provide a minimal reproducible example?
Here you go https://github.com/scottfwalter/spa-test
Hi guys, I managed to get this working.
My setup is as follows:
In root-config:
<!-- this import map is for Angular + rxjs -->
<script type="systemjs-importmap" src="http://localhost:9200/import-map.json"></script>
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "./js/single-spa.min.js",
// ... put apps here
}
}
Then I have a 'shared-deps' module where I repack Angular dependencies using Webpack and generate the import map:
entry: {
'@angular__animations': './node_modules/@angular/animations/fesm2015/animations.js',
'@angular__core': './node_modules/@angular/core/fesm2015/core.js',
'@angular__common': './node_modules/@angular/common/fesm2015/common.js',
'@angular__common__http': './node_modules/@angular/common/fesm2015/http.js',
'@angular__platform-browser': './node_modules/@angular/platform-browser/fesm2015/platform-browser.js',
'@angular__platform-browser-dynamic': './node_modules/@angular/platform-browser-dynamic/fesm2015/platform-browser-dynamic.js',
'@angular__router': './node_modules/@angular/router/fesm2015/router.js',
'rxjs': './node_modules/rxjs/_esm2015/index.js',
'rxjs__operators': './node_modules/rxjs/_esm2015/operators/index.js',
},
output: {
libraryTarget: 'system',
},
externals: [
'@angular/animations',
'@angular/common',
'@angular/common/http',
// '@angular/compiler',
'@angular/core',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'rxjs',
'rxjs/operators',
'zone.js',
],
plugins: [
new ImportMapPlugin({
transformKeys: filename => {
return filename.replace('__', '/').replace(/\.js$/, '');
},
fileName: 'import-map.json',
baseUrl: './'
}),
],
The key here is that you need to run ngcc in the shared-deps module, so that the FESM modules are Ivy compiled.
Do you have a sample repo with this working that we can see?
I extracted my solution and forked the repro case from @scottfwalter - https://github.com/lqc/spa-test
I used webpack just to check if it's possible, but I don't think you can generate reasonable packages for rx.js using webpack, so left that part untouched.
@lqc Thank you for taking the time and creating that demo. It works!
I think I made some progress. I made sure 'single-spa' and 'single-spa-angular' were in my child's app's webpack externals config. However, I now get this error: Unable to resolve bare specifier 'single-spa-angular/internals' .
I do have single-spa and single-spa-angular in my import map:
<script type="systemjs-importmap"> { "imports": { "@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js", "@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js", "@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js", "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js", "rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs.min.js", "rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs-operators.min.js", "single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js", "single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.min.js", "@cxone/app1": "http://localhost:4200/main.js", "@cxone/app2": "http://localhost:4202/main.js" } } </script>
@scottwalter-nice When I faced this error, it was resolved for me by adding an entry for single-spa-angular/internals to the import map as follows:
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
"single-spa-angular/internals": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular-internals.umd.js",
"single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.js",
"@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js",
"@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js",
"@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js",
"@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js",
"rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs.min.js",
"rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs-operators.min.js"
}
}
</script>
Closing this issue as stale. We also have published Angular packages in system format in esm-bundle repo (see single-spa documentation regarding this).