I have recreated the issue with the repo available here: https://github.com/module-federation/module-federation-examples/tree/master/angular13-microfrontends-lazy-components.
The mdmf-shared-service isn't being used by default, so I attempted to use it but notice two seperate instances of the service getting created for mdmf-shell, and mdmf-profile. I've confirmed this by logging the constructor call and some local class values. Constructor is called twice and neither of the values passed to the instance is known to the other app. Here is the code I added - what am I doing wrong?
mdmf-shared.service.ts
import { Injectable } from '@angular/core';
import { MdmfSharedModule } from '../modules/mdmf-shared.module';
@Injectable({
providedIn: MdmfSharedModule, // I've also tried providedIn: 'root' and 'platform' same result
})
export class MdmfSharedService {
private count: number = 0;
private word: string = '';
constructor() {
console.log(this.count++)
}
ping(word?: string) {
console.log('pinging mdmf shared service')
this.word = word;
console.log('this is the word: ', word)
}
}
mdmf-shared.module.ts
import { NgModule } from '@angular/core';
import { MdmfSharedComponent } from '../components/mdmf-shared.component';
import { CommonModule } from '@angular/common';
@NgModule({
// declarations: [MdmfSharedComponent, ListUserComponent],
declarations: [MdmfSharedComponent],
imports: [CommonModule],
// exports: [MdmfSharedComponent, ListUserComponent]
exports: [MdmfSharedComponent],
})
export class MdmfSharedModule {}
app.module(mdmf-shell)
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { ShellModule } from './shell/shell.module';
import { AppComponent } from './app.component';
import { APP_ROUTES } from './app.routes';
import { MicrofrontendService } from './microfrontends/microfrontend.service';
import { MdmfSharedModule } from 'projects/mdmf-shared/src/lib/modules/mdmf-shared.module';
import { NgxsModule } from '@ngxs/store';
import { UserState } from 'projects/mdmf-shared/src/lib/app-state/state/user.state';
export function initializeApp(mfService: MicrofrontendService): () => Promise<void> {
return () => mfService.initialise();
}
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
RouterModule.forRoot(APP_ROUTES),
NgxsModule.forRoot([UserState]),
MdmfSharedModule,
],
providers: [
MicrofrontendService,
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
multi: true,
deps: [MicrofrontendService],
}
],
bootstrap: [AppComponent],
})
export class AppModule {}
app.component.ts(mdmf-shell)
import { Component } from '@angular/core';
import { MdmfSharedService } from 'projects/mdmf-shared/src/public-api';
import { MicrofrontendService } from './microfrontends/microfrontend.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
title = 'mdmf-shell';
constructor(public mfService: MicrofrontendService, public mdmfSharedService: MdmfSharedService) {
mdmfSharedService.ping() // first instance
}
}
app.module.ts(mdmf-profile) no imports of mdmf-shared.module or service
profile.component.ts(mdmf-profile)
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Store } from '@ngxs/store';
import { AddUser } from 'projects/mdmf-shared/src/lib/app-state/actions/user.action';
import { User } from 'projects/mdmf-shared/src/lib/app-state/models/User';
import { MdmfSharedService } from 'projects/mdmf-shared/src/lib/services/mdmf-shared.service';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css'],
})
export class ProfileComponent implements OnInit {
angForm: FormGroup;
ngOnInit(): void {}
constructor(private fb: FormBuilder, private store: Store, public mdmfSharedService: MdmfSharedService) {
mdmfSharedService.ping() // second instance
this.angForm = this.createForm();
}
/**
* Initialize the form
*/
createForm(): FormGroup {
return this.fb.group({
name: ['', Validators.required],
email: ['', Validators.required],
});
}
/**
* Handle the add user when the 'Create User' button is clicked
* @param name: user's name
* @param email: user's email
*/
addUser(name: string, email: string): void {
this.store.dispatch(new AddUser({ name, email } as User));
}
/**
* Get the users for unit testing purposes
*/
getUsers(): User[] {
return this.store.selectSnapshot<User[]>(state => state.users.users);
}
}
webpack.config.js(mdmf-shell)
const webpack = require('webpack');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
output: {
publicPath: 'http://localhost:4200/',
uniqueName: 'shell',
scriptType: 'text/javascript',
},
optimization: {
runtimeChunk: false,
},
plugins: [
new ModuleFederationPlugin({
remotes: {
profile: 'profile@http://localhost:4201/remoteEntry.js}',
},
shared: {
'@angular/core': { eager: true, singleton: true },
'@angular/common': { eager: true, singleton: true },
'@angular/router': { eager: true, singleton: true },
'@ngxs/store': { singleton: true, eager: true },
'mdmf-shared': { singleton: true, eager: true },
},
}),
],
};
webpack.config.js(mdmf-profile)
const webpack = require('webpack');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
output: {
publicPath: 'http://localhost:4201/',
uniqueName: 'mdmfprofile',
scriptType: 'text/javascript',
},
optimization: {
runtimeChunk: false,
},
plugins: [
new ModuleFederationPlugin({
name: 'profile',
library: { type: 'var', name: 'profile' },
filename: 'remoteEntry.js',
exposes: {
ProfileModule: './projects/mdmf-profile/src/app/profile/profile.module.ts',
},
shared: {
'@angular/core': { singleton: true, eager: true },
'@angular/common': { singleton: true, eager: true },
'@angular/router': { singleton: true, eager: true },
'@ngxs/store': { singleton: true, eager: true },
'mdmf-shared': { singleton: true, eager: true },
},
}),
],
};