diff --git a/angular15-microfrontends-lazy-components/package.json b/angular15-microfrontends-lazy-components/package.json index a39ba8cadab..a3cace4ea2a 100644 --- a/angular15-microfrontends-lazy-components/package.json +++ b/angular15-microfrontends-lazy-components/package.json @@ -11,7 +11,7 @@ "start:profile": "ng serve mdmf-profile", "build:shared": "ng build mdmf-shared", "build:shell": "ng build mdmf-shell", - "build:profile": "ng build mdmf-profile --prod", + "build:profile": "ng build mdmf-profile", "test": "ng test mdmf-shared & ng test mdmf-profile & ng test mdmf-shell", "lint": "ng lint", "e2e": "ng e2e mdmf-profile & ng e2e mdmf-shell", diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.html b/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.html index 6b0b085c195..f7c594b21d0 100644 --- a/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.html +++ b/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.html @@ -1,5 +1,7 @@

List users from the shared application state

+

{{ label }}

+

{{ foo }}

@@ -13,10 +15,10 @@

List users from the shared application state

- +
{{ user.name }} {{ user.email }} + -
-
\ No newline at end of file + diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.ts b/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.ts index 4a74121b49a..46dad4caa57 100644 --- a/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.ts +++ b/angular15-microfrontends-lazy-components/projects/mdmf-profile/src/app/profile/components/list-user/list-user.component.ts @@ -1,4 +1,8 @@ -import { Component, OnInit } from '@angular/core'; +import { + Component, + Input, + OnInit, +} from '@angular/core'; import { Select, Store } from '@ngxs/store'; import { RemoveUser } from 'projects/mdmf-shared/src/lib/app-state/actions/user.action'; import { User } from 'projects/mdmf-shared/src/lib/app-state/models/User'; @@ -14,7 +18,11 @@ import { Observable } from 'rxjs'; export class ListUserComponent implements OnInit { @Select(UserState.getUsers) users: Observable; - constructor(private store: Store) {} + @Input() label = ''; + @Input() foo = ''; + constructor(private store: Store) { + console.log('Federated component Created'); + } ngOnInit(): void {} diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/federated/federated.component.ts b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/federated/federated.component.ts index 0dc7d3f00d5..5b9b332efce 100644 --- a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/federated/federated.component.ts +++ b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/federated/federated.component.ts @@ -8,6 +8,7 @@ import { ɵcreateInjector, } from '@angular/core'; import { loadRemoteModule } from '../../../utils/federation-utils'; +import { ListUserComponent } from '../list-user/list-user.component'; @Component({ selector: 'federated-component', @@ -21,6 +22,16 @@ export class FederatedComponent implements OnInit { @Input() remoteName: string; @Input() exposedModule: string; @Input() componentName: string; + @Input() set props(props: Record) { + if (this.componentRef) { + this.updateComponentProps(props); + } + + this._props = props; + }; + + private componentRef = null; + private _props = null; constructor(private injector: Injector) {} ngOnInit(): void { @@ -29,10 +40,24 @@ export class FederatedComponent implements OnInit { remoteName: this.remoteName, exposedModule: this.exposedModule, }).then(federated => { - const { instance } = this.federatedComponent.createComponent( + const componentRef = this.federatedComponent.createComponent( federated[this.exposedModule].exports.find(e => e.ɵcmp?.exportAs[0] === this.componentName), { injector: ɵcreateInjector(federated[this.exposedModule], this.injector) }, ); + + this.componentRef = componentRef; + + if (this._props) { + this.updateComponentProps(this._props); + } }); } + + private updateComponentProps(props: Record) { + if (props && this.componentRef) { + Object.entries(this._props).forEach(([key, value]) => { + this.componentRef.setInput(key, value); + }); + } + } } diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.html b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.html index 8688e646201..26228938bf8 100644 --- a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.html +++ b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.html @@ -1,10 +1,22 @@
-

Microfrontend Shell

+

Welcome to the Angular 15 Microfrontend demo using Webpack 5 Module Federation

This component is part of the shell application, the Profile component that is linked from the `Profile` link at the top is a Microfrontend that is remotely loaded into the application. Check the network settings to see the remote being loaded.


- + Federated Component + +
+ +
+ Regular Component + +
diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.ts b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.ts index 796fdc614e5..c9321d79fee 100644 --- a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.ts +++ b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/home/home.component.ts @@ -6,6 +6,13 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./home.component.scss'], }) export class HomeComponent implements OnInit { + federatedLabel = 'Federated Label'; + counter = 0; constructor() {} ngOnInit(): void {} + + someEvent() { + this.counter += 1; + this.federatedLabel = 'Federated Label' + this.counter; + } } diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.css b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.html b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.html new file mode 100644 index 00000000000..7aff26cef80 --- /dev/null +++ b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.html @@ -0,0 +1,24 @@ +
+

{{ label }}

+

{{ foo }}

+

List users from the shared application state

+
+ + + + + + + + + + + + + + + +
NameEmailAction
{{ user.name }}{{ user.email }} + +
+
diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.spec.ts b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.spec.ts new file mode 100644 index 00000000000..04b9d12b442 --- /dev/null +++ b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.spec.ts @@ -0,0 +1,55 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgxsModule } from '@ngxs/store'; +import { ProfileComponent } from 'projects/mdmf-profile/src/app/profile/components/profile/profile.component'; +import { User } from 'projects/mdmf-shared/src/lib/app-state/models/User'; +import { UserState } from 'projects/mdmf-shared/src/lib/app-state/state/user.state'; +import { MdmfSharedModule } from 'projects/mdmf-shared/src/lib/modules/mdmf-shared.module'; +import { ListUserComponent } from './list-user.component'; + +describe('ListUserShellComponent', () => { + let component: ListUserComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + ReactiveFormsModule, + FormsModule, + MdmfSharedModule, + NgxsModule.forRoot([UserState]), + ], + declarations: [ListUserComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ListUserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should render h2 element', () => { + const element = fixture.debugElement.nativeElement.querySelector('h2'); + expect(element.textContent).toContain('List users from the shared application state'); + }); + + it('should remove an User from the store', () => { + const user: User = { name: 'Mr. A', email: 'a@company.com' }; + + // add User into the store + const profileComponent = TestBed.createComponent(ProfileComponent).componentInstance; + profileComponent.addUser(user.name, user.email); + expect( + component.getUsers().filter(u => u.name === user.name && u.email === user.email)[0], + ).toEqual(user); + + // remove the User from the store + component.removeUser(user); + expect(component.getUsers().length).toEqual(0); + }); +}); diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.ts b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.ts new file mode 100644 index 00000000000..229b7800faa --- /dev/null +++ b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/components/list-user/list-user.component.ts @@ -0,0 +1,43 @@ +import { + Component, + Input, + OnInit, +} from '@angular/core'; +import { Select, Store } from '@ngxs/store'; +import { RemoveUser } from 'projects/mdmf-shared/src/lib/app-state/actions/user.action'; +import { User } from 'projects/mdmf-shared/src/lib/app-state/models/User'; +import { UserState } from 'projects/mdmf-shared/src/lib/app-state/state/user.state'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'shell-profile-list-user', + templateUrl: './list-user.component.html', + styleUrls: ['./list-user.component.css'], +}) +export class ListUserComponent implements OnInit { + @Select(UserState.getUsers) users: Observable; + + @Input() label = ''; + @Input() foo = ''; + + constructor(private store: Store) { + console.log('Regular component Created'); + } + + ngOnInit(): void {} + + /** + * Handle the remove user when the "Remove User" button is clicked + * @param user: the user info + */ + removeUser(user: User): void { + this.store.dispatch(new RemoveUser(user)); + } + + /** + * Get the users for unit testing purposes + */ + getUsers(): User[] { + return this.store.selectSnapshot(state => state.users.users); + } +} diff --git a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/shell.module.ts b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/shell.module.ts index 31d95894e97..b6f9ed2d840 100644 --- a/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/shell.module.ts +++ b/angular15-microfrontends-lazy-components/projects/mdmf-shell/src/app/shell/shell.module.ts @@ -5,9 +5,10 @@ import { ProfileRoutingModule } from './shell-routing.module'; import { HomeComponent } from './components/home/home.component'; import { FederatedComponent } from './components/federated/federated.component'; import { MdmfSharedModule } from 'projects/mdmf-shared/src/lib/modules/mdmf-shared.module'; +import { ListUserComponent } from './components/list-user/list-user.component'; @NgModule({ - declarations: [HomeComponent, FederatedComponent], + declarations: [HomeComponent, FederatedComponent, ListUserComponent], imports: [CommonModule, ProfileRoutingModule, FormsModule, ReactiveFormsModule, MdmfSharedModule], }) export class ShellModule {}