Open deepa-7k opened 2 years ago
I covered this in my blog. Check this out: https://medium.com/p/258f331bc11e
We tried the same thing mentioned in the blog in the config file but that does not solve the issue Should sharedMappings.getPlugin() be removed?
@deepa-7k. It happens when you load Angular more than once. In other words, you load different versions of Angular. Checkout this config from https://github.com/vugar005/youtube-webapp-lerna/blob/master/likes-app/src/bootstrap.ts
As @vugar005 says. You are very likely using different (perhaps only slightly different) versions of Angular (due to package.json or package-lock.json)
Thank you for the response. We will check the config which you have shared. Also just wanted to know, if this inject error occurs due to different versions of Angular packages, will it occur every time we try to load the MFE? What we face is something that occurs very randomly and mostly not reproducible with manual testing.
hello i'm having the same issue using nx repo with angular/core 12.2.16
core.js:6498 ERROR Error: Uncaught (in promise): Error: inject() must be called from an injection context
Error: inject() must be called from an injection context
at injectInjectorOnly (core.js:4764:15)
at Module.ɵɵinject (core.js:4774:12)
at Object.RouterModule_Factory [as factory] (router.js:6002:102)
at R3Injector.hydrate (core.js:11457:35)
at R3Injector.get (core.js:11276:33)
at core.js:11314:55
at Set.forEach (
@deepa-7k yes it would happen whenever a new Angular app is going to be bootstrapped.
If I understand correctly: if we want to use some shared angular lib that calls inject() funcion there is NO WAY we can run multiple different versions of Angular (like explained in this article https://www.angulararchitects.io/aktuelles/multi-framework-and-version-micro-frontends-with-module-federation-your-4-steps-guide/). In that case all micro frontend apps must be build on the same Angual version? Is that right @vugar005, @manfredsteyer?
My example: this error Error: NG0203: inject() must be called from an injection context
happened to me when I tried to use shared angular library @swimlane/ngx-datatable
in angular 13 shell app and in angular 12 remote app. I setup both apps according to Manfred Steyer's "multiple framework versions" article mentioned above. If I only use core angular libraries (different versions for shell and remote app) it works fine. But when I add ngx-datatable
shared libary I get that inject() error. Is there no solution for this problem (allowing to use different angular versions)?
UPDATE:
I have fixed my problem by removing @swimlane/ngx-datatable
from shared module (in module federation config for my angular 12 remote app). This has caused that the remote app is using a separate instance of module, which is apparently linked to angular 12 core libraries and works fine. Disadvantage is that ngx-datatable module must be bundled and downloaded with every remote app that is running different version of angular than shell app.
UPDATE 2:
I have found out this can be solved more elegantly by using shareKey
option in ModuleFederationPlugin (https://webpack.js.org/plugins/module-federation-plugin/#sharekey). For each shared library that is causing conflicts we can define different shareKey value for each angular version. So module federation will only instantiate one instance per angular version, which will still be quite efficient.
I have tested by using this configuation in wepack.config.js under ModuleFederationPlugin:
Config for host app (running angular 13):
shared: share({
'@angular/core': { shareKey: '@angular/core for angular 13', requiredVersion: 'auto', singleton: true },
...
'@swimlane/ngx-datatable': { shareKey: '@swimlane/ngx-datatable for angular 13', requiredVersion: 'auto', singleton: true }
})
Config for remote app (running angular 12):
shared: share({
'@angular/core': { shareKey: '@angular/core for angular 12', requiredVersion: 'auto', singleton: true },
...
'@swimlane/ngx-datatable': { shareKey: '@swimlane/ngx-datatable for angular 12', requiredVersion: 'auto', singleton: true }
})
With this configuration, host app downloads ngx-datatable module, and also remote app downloads it and use it's own instance. Works brilliant. BTW: I also tried to use shareScope
option instead of shareKey
but it didn't work (it loaded the same module with same shareScope twice which is not as expected; maybe I don't understand what shareScope does, or maybe this is related to shareScope bug - https://github.com/webpack/webpack/issues/15769).
Hi jure123 ... can you maybe explain a bit more of steps how you solved multiple angular (core) versions at the same time? My shell is running for example with A16 and my MFEs are under A15. So I am getting thus mentioned error above Error: inject() must be called from an injection context ...
is there a way with shareKey
or shareScope
from webpack only to get this working?
So I am getting thus mentioned error above
Error: inject() must be called from an injection context ...
is there a way withshareKey
orshareScope
from webpack only to get this
@ArgV04, my example mentioned two comments above solved my problem. I have posted config for host/shell app that is running Angular 13 and useing ngx-datatable 13; and config for remote apps that are running on Angular 12 and using ngx-datatable 12.
You shall probably use similar config, but instead of ngx-datatable (that was causing the issue in my project) you should define library or more libs that are causing inject() error (I think you can find it in error stack trace, but I forgot). If you can't find the libs you can try to add to both configs (or only remote config maybe) all libs that each project is using. Make sure shareKey has different value in shell and remote config. If you put in shell config all libs that shell is using, and in remote config all libs remote is using, and define different shareKey values, then I believe it should work, because remote federation will make sure that each project only uses their own libs.
I haven't tested this in later Angular versions, I hope they don't cause some additional issues.
Yeah I tried your config ... and here is an error log output for example :) ... so I think shareKey is maybe working somehow but Angular uses some globalState dependencies, liked mentioned here. So just wondering how this was working on your side, when trying to initialize multiple angular/core, zone dependencies trying to setup multiple global states at runtime ...
From your error stack trace it seems there is issue with RouterModule. Did you try to use "solution 3" mentioned here (the article you already posted).
mfe1 webpack config
shared: {
"@angular/core": {requiredVersion: '13.1.1'},
"@angular/common": {requiredVersion: '13.1.1'},
"@angular/router": {requiredVersion: '13.1.1'},
"@angular/common/http": {requiredVersion: '13.1.1'}
}
I guess in your case shell config should have requiredVersion 16, remote should have 15. Apparently in my case I haven't had this issue because I didn't use conficting providers from @angular/router, or maybe there was not conflict between Angular 12 and 13 that I used.
Our team found these kind of version conflicts too risky (and unpredictable) for the serious long term use of module federation for larger projects (unless we could agree that we always updated shell and all remotes to the same Angular version, which is not possible for larger long term projects).
I am currently also evaluating using module federation for our products.
A simple test trying to use an mfe with angular 16 in a shell with angular 17 fails with this mentioned error.
Is this simply not possible? Our intention is to be able to update our products gradually to new versions which would not be possible if we cannot get different versions to work with each other.
@jure123 Have you found other approaches using different versions for large projects?
@digaus, as I mentioned two comments above, I think that possiblilty of different Angular versions conflict is too unpredictable and too risky to use MFE concept in large-scale long term projects. The risk is not only with differnt versions of Angular core libraries, but also with different versions of other 3rd party UI libraries that may cause incompatible JS or even CSS.
In our large project we have decided to develop different custom approach that we called "Multi-SPA", where different MFE apps are built separately (like standard SPA), but not with header, footer and navigation menu, only their content part. Then we built a special "Layout app" that is loaded in each SPA (as a single JS file), which renders header/footer/menu around the SPA content. Each SPA is deployed in a separate folder (/mfe1, /mfe2, ...). When user jumps from one MFE to another the browser it's similar like your would jump from one web site to another: the first SPA is destroyed from browser DOM completely (HTML/JS/CSS), and the new MFE app is rendered from stratch. But both load the same Layout script that renders the same Layout, so the user experiences seprate apps to look and feel as one portal.
The advantage of our approach is absolutely no risk of conflicts between MFE apps (we tested with Angular 14,15,16, Angular Universal, React, Vue), and each SPA can use it's own 3rd party libs without in any versions.
The down side is that switching MFE apps takes a performance hit, as all JS bundles have to be loaded and parsed from stracth. But in practice, only the first load takes a bit more time, after that browser loads JS/CSS from disk or memory cache and it works quite fast (although not as smootly as in the classic MFE shell-remote concept).
We use this approach for CRM portal which is assembled with many separate SPAs that load the same "Layout script", and we are very pleased with the solution. However it took quite some investigations and custom solutions how to communicate between SPA and our Layout app (who controls user authentication, how navigation menu causes route change when jumping pages inside one SPA or jumping to different SPA, etc.). No simple MFE solution in the world yet :)
We are getting this error also when using native-federation with webcomponents. While we thought this is the most robust / independent variant, the following situation leads to the error:
In this case, both versions of angular will be loaded (as expected). Also only a single version of primeng will be loaded (shared, since the version is the same).
But now primeng tries to call inject, in both shell and mfe. One of those will fail, since they target different versions of angular and different injection context.
To solve this it would be helpful to be able to couple the sharing of angular and a lib like primeng: Share the lib version if and only if the angular version is also shared. Use a separate bundle of the lib, even if the version is the same, if the angular version is not shared. While in module federation this could be solved with a share key, if I understand the above comment correctly, I do not see any option for this in native federation.
Any update about this topic ? @angelo-v @manfredsteyer @gobedson @digaus @jure123 ? We are also trying to make native federation work with host and remote having both a specific version of angular and we are unable to make them not conflict with each other.
Are there any fixes available or incoming ? Any alternative ? Should we use module federation instead ?
Did you can tried this --> https://github.com/angular-architects/module-federation-plugin/issues/47#issuecomment-845145887 ... this was working for me when wrapping my MFEs into web components
@ArgV04 Thanks for your quick response, I tried adding these few lines of code on host and remote but still no success, would you mind sharing your host and remote working configuration ? Maybe I'm doing something bad there .. tried many different things
From what i've experimented with different angular versions it all has to do with the requiredVersion
tag in the webpack.config.js
auto
. it will work with other MFEs that are on the same angular version only. Error: NG0402: A required Injectable was not found in the dependency injection tree. If you are bootstrapping an NgModule, make sure that the BrowserModule is imported.
When trying to load the same angular version mfes.^16.0.0
) it won't load at all with the error: Error: Shared module is not available for eager consumption: 69582
This is what i've found that works.
sharedScope
to the particular angular version: (ex. shareScope: '16'
)sharedScope
on the mfe to the same thing based on the angular version. bootstrap.ts
to this.
const platform = getPlatform() || platformBrowser();
platform.bootstrapModule(AppModule, { ngZone: getZone() })
.catch(err => console.error(err));
I was using https://github.com/angular-architects/module-federation-plugin/issues/47#issuecomment-845145887 but it stopped working after angular 16 upgrade. This fixed it.
After that i'm able to load a mfe that is angular 12 and one that is angular 16 from an angular 16 shell without a problem.
Hello everyone!
(Please read the preface for details.. The issue got resolved with a minor change)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const mf = require('@angular-architects/module-federation/webpack');
const path = require('path');
const sharedMappings = new mf.SharedMappings(); sharedMappings.register(path.join(__dirname, 'tsconfig.json'), []);
module.exports = { output: { uniqueName: 'PqrWebapp', publicPath: 'auto', }, optimization: { runtimeChunk: false, }, plugins: [ new ModuleFederationPlugin({ name: 'pqrWebapp', filename: 'pqrRemoteEntry.js', exposes: { // Exposed Module }, shared: { '@angular/core': { singleton: true, eager: true }, '@angular/common': { singleton: true, eager: true }, '@angular/router': { singleton: true, eager: true }, '@ngx-translate/core': { singleton: true, eager: true }, 'apollo-angular': { singleton: true, eager: true }, 'apollo-angular-link-http': { singleton: true, eager: true }, 'apollo-client': { singleton: true, eager: true }, '@ngrx/effects': { singleton: true, eager: true }, '@ngrx/store': { singleton: true, eager: true }, rxjs: { singleton: true, eager: true }, 'rxjs/operators': { singleton: true, eager: true }, ...sharedMappings.getDescriptors(), }, }), sharedMappings.getPlugin(), ], };
### ISSUE:
- When we click both the buttons in any order, both the module will fetched by module federation using same remoteEntry of xyz but for the later request module, I'm receiving following error and after that, I won't be able to load any module (neither first, nor the second).
ERROR Error: Uncaught (in promise): Error: inject() must be called from an injection context
Error: inject() must be called from an injection context
at injectInjectorOnly (core.js:4722:1)
at Module.ɵɵinject (core.js:4732:1)
at Object.GraphqlModule_Factory [as factory] (graphql.module.ts:22:27)
at R3Injector.hydrate (core.js:11412:1)
at R3Injector.get (core.js:11232:1)
at core.js:11269:1
at Set.forEach (
- For Module.ɵɵinject mentioned in the error, I debugged and found out that the error is being thrown for Apollo as mentioned in the image.
<img width="788" alt="Screenshot 2024-10-23 at 1 26 46 PM" src="https://github.com/user-attachments/assets/28a87c71-c926-4786-96cb-69c164b54fa0">
- GraphqlModule in each mfe uses `Apollo` from `apollo-angular` & ApolloModule is imported
"apollo-angular": "^1.9.0",
"apollo-angular-link-http": "^1.11.0",
"apollo-cache-inmemory": "^1.6.6",
"apollo-client": "^2.6.10",
"apollo-link": "^1.2.14",
"apollo-link-context": "^1.0.20",
"apollo-link-error": "^1.1.13",
@NgModule({ exports: [HttpClientModule, ApolloModule, HttpLinkModule], }) export class GraphqlModule { constructor( readonly apollo: Apollo, // Other Dependencies ){} // Logic }
- As the modules are loading properly when the btns are clicked mutually exclusively, I strongly believe this is not an angular version issue but can't really figure out what could be the root cause.
- Thanking you for reading this huge chunky passage.. Any help would be highly appreciated 🙏
We get the below error when trying to call a microfrontend. The issue occurs only at times and fails to load the MFE
main.52b1fee5a0c17f23f7ca.js:1 ERROR Error: Uncaught (in promise): Error: inject() must be called from an injection context Error: inject() must be called from an injection context at _r (remoteEntry.js:471:169) at wr (remoteEntry.js:471:342) at Module.Ec (remoteEntry.js:902:157) at po.e.ɵfac [as factory] (934.c221672f75e97217c5b8.js:1:7157) at _t (main.52b1fee5a0c17f23f7ca.js:1:142547) at main.52b1fee5a0c17f23f7ca.js:1:192374 at Wb (main.52b1fee5a0c17f23f7ca.js:1:192445) at gh.create (main.52b1fee5a0c17f23f7ca.js:1:276234) at py.createComponent (main.52b1fee5a0c17f23f7ca.js:1:253831) at h. (405.627f7ca446d7f03d3443.js:1:6554)
at rn (polyfills.6a402a4b8539d95a42ad.js:1:1740824)
at polyfills.6a402a4b8539d95a42ad.js:1:1739784
at R (405.627f7ca446d7f03d3443.js:1:95547)
at ln.invoke (polyfills.6a402a4b8539d95a42ad.js:1:1730740)
at Object.onInvoke (main.52b1fee5a0c17f23f7ca.js:1:304055)
at ln.invoke (polyfills.6a402a4b8539d95a42ad.js:1:1730674)
at Ln.run (polyfills.6a402a4b8539d95a42ad.js:1:1725789)
at polyfills.6a402a4b8539d95a42ad.js:1:1741725
at ln.invokeTask (polyfills.6a402a4b8539d95a42ad.js:1:1731392)
at Object.onInvokeTask (main.52b1fee5a0c17f23f7ca.js:1:303871)
Tv @ main.52b1fee5a0c17f23f7ca.js:1
We tried giving the exact version of Angular packages required in our webpack configs both in shell and child applications and also tried using singleton but none of them has solved the issue. Please advise on what can be done or what could be the issue here