question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unable to share singleton from local Angular Library

See original GitHub issue

I can’t figure out how to share the same instance of an injectable service across federated Angular modules in a CLI project. I have a Git Repo that illustrates my problems.

I started from a working example, then generated a new lib (ng generate library mdmf-shared) and exposed a single Injectable from the library’s public-api. I import this service into my shell and my microfrontend with no problem, but the microfrontend creates a new instance when it loads.

I assumed that I may just need to add the dependency to my ‘shared’ configuration in my webpack.config.ts files, but It just ends up with compilation errors.

  plugins: [
    new ModuleFederationPlugin({
      shared: {
        "@angular/core": { eager: true, singleton: true },
        "@angular/common": { eager: true, singleton: true },
        "@angular/router": { eager: true, singleton: true },
        "mdmf-shared": { eager: true, singleton: true },
      },
    }),
  ],

if I do something like the above, I end up with an error during compile. I have tried all sorts of values using all sorts of options listed in node_modules/webpack/schemas/plugins/sharing/SharePlugin.json, but I’m clearly out of my depth here.

Error: Module not found: Error: Can't resolve 'mdmf-shared' in '/Users/gallou/WebstormProjects/module-federation-examples/angular11-microfrontends/projects/mdmf-shell/src/app/home'

As you can tell from my examples above, I have tried making a repro project based on your angular11 example, but my repo is based on Manfred Steyers example. Both projects act the same for me once I try adding the library.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:9

github_iconTop GitHub Comments

4reactions
colinfindlaycommented, Oct 27, 2020

I also struggled with this however I ended up getting it working. I have committed the change I made to my fork here in case that is useful for you/others

https://github.com/colinfindlay/module-federation-examples/commit/45faa1d4b637fb08eb4e3bd917e21cc8fa85164e#diff-7b53afde1320d2f674223b51cd559944ca60c6722fa481c4cc50bb57385b6c46

Incidentally what led me down this path was re-reading this part of the manual ( https://webpack.js.org/concepts/module-resolution/ )- and realising I needed to swap a module path with a relative path as the module is in my Angular workspace rather than in node_modules/package.json.

In my experience the micro-front ends often require core data or context that can be shared across apps. I think having “shell level singleton services” is an essential pattern for core data and contextual services in this micro-frontend use case - and as such it this is a useful demo. Happy to contribute this back to the examples if others agree.

2reactions
mgrespagecommented, Oct 28, 2020

Just adding some notes here in case anyone else lands on this page.

My project is an NX Monorepo using Angular CLI. I’m using (at the moment) Angular 11.0.0-next and Webpack 5, which is only available as an opt-in with ng11 at the time of writing.

If you’re on NX, your linting will complain if you import from absolute or relative library paths, so there is a disconnect between what Webpack wants, and what tslint wants. Paraphrasing the important bits of colinfindlay’s answer, you can get singletons to work across federated modules by sharing the module in your webpack.config.ts file. Extending his example to use the CLI, your setup could end up something like this

tsconfig.base.json
...
"paths": {
    "@common/facades": [
        "libs/common/facades/src/index.ts"
    ],
    "@common/data-access": [
        "libs/common/data-access/src/index.ts"
    ]
}
apps/shell/webpack.config.js
...
plugins: [
    new ModuleFederationPlugin({
        remotes: {
            'dashboard': 'dashboard@http://localhost:8010/remoteEntry.js',
            'reputation': 'reputation@http://localhost:8020/remoteEntry.js'
        },
        shared:         {
            "@angular/core": {
                "singleton": true
            },
            "@angular/common": {
                "singleton": true
            },
            "@angular/router": {
                "singleton": true
            },
            "@angular/material/icon": {
                "singleton": true
            },
            "@common/data-access": {
                "singleton": true,
                "import": "libs/common/data-access/src/index"
            },
            "@common/facades": {
                "singleton": true,
                "import": "libs/common/facades/src/index"
            },
            "rxjs": {
                "singleton": true
            },
            "ramda": {
                "singleton": true
            }
        }
    })
]

It turns out I was having two problems. First, Webpack has to know how to identify the modules you are trying to share, and using the paths from your tsconfig doesn’t work. Webpack needs to know the path from root, but your apps and libs should reference the path from your tsconfig files. You can use the ‘imports’ option of a shared object to accomplish this

webpack.config.ts
 "@common/data-access": {
        "singleton": true,
        "import": "libs/common/data-access/src/index"
 },

Here, we refer to our library by its alias path from tsconfig, but we tell Webpack where the library is actually located via the import option. This solved the ‘Module not found’ errors I was getting during compile.

My second problem was that I was attempting to share common facades depend on common data access services. I didn’t think to share the data access services because they are stateless, but this caused my MFEs to instantiate new singletons. Sharing my data access layer along with my facade layer resulted in shared singletons throughout my app.

The issue of dependencies is the most important thing here, because it’s harder to debug. There are no errors or anything – things just don’t work like you expect. You may not even realize that you have two instances of a shared service at first. So, if you run into this issue take a look at your dependencies and make sure you’re sharing everything you need. This is probably an argument for keeping minimal shared state/dependencies across your apps.

  • a quick note about @angular/material/icon share config. In my app I populate the MatIconRegistry with icons that every app needs. I had to share the @angular/material/icon specifically to share the singleton across every MFE.

Closing this issue. Resolved.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Module Federation Shared Services - angular - Stack Overflow
I can't figure out how to share a singleton service from a shared local Angular library between my two apps. I'll do my...
Read more >
Singleton services - Angular
A singleton service is a service for which only one instance exists in an application. For a sample application using the app-wide singleton...
Read more >
Providing dependencies in modules - Angular
With providedIn: 'any' , all eagerly loaded modules share a singleton instance; however, lazy loaded modules each get their own unique instance, as...
Read more >
Sharing modules - Angular
Creating shared modules allows you to organize and streamline your code. You can put commonly used directives, pipes, and components into one module...
Read more >
Usage of Angular libraries published to npm
Typically, library packages include typings in .d.ts files; see examples in node_modules/@angular/material . If the package of your library does not include ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found