(No Ratings Yet)
Loading...

Rarely, but every once in a while you will run into the need to have multiple root angular modules with two-way communication between them. A good example would be embedding anuglar into a static webpage for only certain portions of the page.

The solution? A shared singleton service!

In the following example we take a not-so-conventional case of having a navigation and the content both as a single independent root module.

The folder structure is the following:

/src
----/index.html
----/app
--------/app.module.ts
--------/nav
------------/nav.module.ts
------------/nav.component.ts
------------/nav.component.html
--------/content
----------------/content.module.ts
----------------/content.component.ts
----------------/content.routing.ts
----------------/content.component.html
----------------/about-me
-------------------------/about-me.component.ts
-------------------------/about-me.component.html
----------------/contact
------------------------/contact.component.ts
------------------------/contact.component.html
--------/shared
---------------/shared.service.ts

Your html would contain multiple root modules, as many as you want, in this example two (ContentModule and NavModule).

Declaring the root modules is easy, in our example we have ContentModule and NavModule. They are provided by SharedService which will take care of communication back/forth, which we will describe later on. These two separate modules are then bound to two different html tags:

Index.html:

<app-nav>Navigation</app-nav>
<app-base>Content</app-base>

You would also need a shared service, which would then register as a provider for each of your ‘root’ modules and would reside somewhere in the app root. In the following example what is paassed back and forth with the help of rxjs is merely a string, but it could be anything.

shared.service.ts:

import { Injectable } from '@angular/core';
import { Subject , Observable} from 'rxjs';
 
@Injectable()
export class SharedService {
 
    private setClickSource = new Subject<string>();
    setClick$ = this.setClickSource.asObservable();
 
    setClick(source: string) {
        this.setClickSource.next(source);
    }
}

And now one of the two modules declaration with a provider of this service.

content/content.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {ContentComponent} from './content.component';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import { ContentRoutingModule } from './content.routing';
import {RouterModule} from '@angular/router';
import {AboutMeComponent} from './about-me/about-me.component';
import {ContactComponent} from './contact/contact.component';
import {SharedService} from '../shared/shared.service';
 
@NgModule({
  imports:      [ 
    NgbModule,
    ContentRoutingModule,
    RouterModule,
  ],
  bootstrap:    [
  ],
  declarations: [
    ContentComponent,
    AboutMeComponent,
    ContactComponent
  ],
  providers: [
    SharedService
  ]
})
export class ContentModule { }

In the same way we would create the navigation module:

nav/nav.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {NavComponent} from './nav.component';
import {SharedService} from '../shared/shared.service';
@NgModule({
  imports:      [ 
    NgbModule,
  ],
  declarations: [
    NavComponent
  ],
  providers: [
    SharedService
  ]
})
export class NavModule { }

And now an example how to communicate from navigation to content. The navigation component has come click events which will later be sent to the shared service.

nav/nav.component.html:

<ngb-tabset>
  <ngb-tab>
      <ng-template ngbTabTitle>
        <div (click)="clicked('about-me')">
          About me
        </div>
      </ng-template>
  </ngb-tab>
  <ngb-tab>
      <ng-template ngbTabTitle>
        <div (click)="clicked('contact')">
          Contact
        </div>
      </ng-template>
  </ngb-tab>
</ngb-tabset>

And in the component we catch the click events and send them to the shared service.

nav/nav.component.ts:

import {Component} from '@angular/core';
import {SharedService} from '../shared/shared.service';
import {Router} from '@angular/router';
 
 
@Component({
  selector: 'app-nav',
  templateUrl: './nav.component.html',
  styleUrls: ['./nav.component.css']
})
export class NavComponent {
    constructor(private _sharedService: SharedService,
                private router: Router) {
    }
 
    clicked(source) {
      this._sharedService.setClick(source);
    }
}

Now we have to catch the events somewhere and we will do that in the ContentComponent. In the following example the ContentComponent does some internal routing, which is entirely encompassed by the ContentModule and separate from anything else. I will not describe that part in detail, but there is a link to the StackBlitz down below containing the full code.

content/content.component.ts:

import {Component, NgZone} from '@angular/core';
import {SharedService} from '../shared/shared.service';
import {Router} from '@angular/router';
 
@Component({
    selector:'app-base',
    templateUrl: './content.component.html',
    styleUrls: ['./content.component.css']
})
export class ContentComponent {
 
    constructor(
        private _sharedService: SharedService,
        private router: Router,
        private zone: NgZone
    ) {
        _sharedService.setClick$.subscribe(
            (source) => this.navigateToSource(source),
            e => console.log(e),
        );
    }
 
    navigateToSource(source) {
        if (source === 'about-me') {
            this.zone.run(() => {
                this.router.navigate(['/about-me']);
            });
        } else if (source === 'contact') {
            this.zone.run(() => {
                this.router.navigate(['/contact']);
            });
        }
    }
}

Your base module would then bootstrap both of the root modules.

app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {ContentComponent} from './content/content.component';
import {NavComponent} from './nav/nav.component';
import {ContentModule} from './content/content.module';
import {NavModule} from './nav/nav.module';
 
@NgModule({
  imports:      [ 
    BrowserModule,
    ContentModule,
    NavModule
  ],
  bootstrap:  [ 
    ContentComponent, 
    NavComponent 
  ]
})
export class AppModule { }

You can find the full example @ Stackblitz (https://stackblitz.com/edit/angular-q86nji)

Your email is kept private. Required fields are marked *