Simon
Simon

Reputation: 147

Hide element if named router-outlet has active route

We are currently developing an Angular 13 application that is split into several independent modules that are maintained by different teams and each module represents the UI for a different application that is backed by its own microservice. These modules are allowed to depend on our "SharedModule", but not the other way around.

Now, we want to implement a "Help Center" which consists of a question mark button that floats in the lower right corner and if clicked provides help for the currently loaded page. The thing is, that we want to keep the content of each modules Help Center pages inside that module, furthermore, some modules do not need Help Center pages.

Our current solution adds a component to our main layout:

<router-outlet name="navbar"></router-outlet>

<router-outlet></router-outlet>

<app-help></app-help>

<router-outlet name="footer"></router-outlet>

The app-help component has said floating button,

<button mat-fab (click)="openHelpCenter()">
  <mat-icon>help</mat-icon>
</button>

that opens a Material dialog with a named router-outlet inside:

@Component({
  selector: 'app-help',
  templateUrl: './help.component.html',
  styleUrls: ['./help.component.scss']
})
export class HelpComponent {


  constructor(
    private dialog: MatDialog,
  ) {
  }

  openHelpCenter() {
    this.dialog.open(HelpDialogComponent, {
      width: '300px'
    })
  }
}

@Component({
  selector: 'app-help-dialog',
  template: '<router-outlet name="help"></router-outlet>'
})
export class HelpDialogComponent {

  constructor() {
  }
}

Inside each module the teams may now implement routes that use the named router-outlet:

...
{
        path: '',
        component: HelpComponent,
        outlet: 'help'
}
...

which "injects" the modules HelpComponent inside the Dialog according to the currently loaded url.

This works fairly well, however, we have an open problem:

How to we hide the floating button if the router-outlet is empty, i.e. if you are on a page that does not provide help?

I believe the easiest solution (i.e. the solution that manages stuff with minimal amount of code inside the modules) would be to listen to Router events inside HelpComponent, e.g. NavigationEnd, and then find out whether there is an "active" routing for the "help"-router-outlet and set visibility accordingly. (similar to the RouterLinkActive-Directive)

However, I am not able to find out how to retrieve this information from the Router-Framework.

Of course, I would also appreciate ideas on how to tackle the problem differently.

Upvotes: 0

Views: 3611

Answers (3)

urDMG
urDMG

Reputation: 436

There is another solution by using RouterLinkActive directive

<div routerLink="/specific/route" routerLinkActive="d-none">
   ... show/hide content on a specific route ... 
</div>

style.scss

.d-none {
  display: none
}

Another way is to use template variable

<div routerLink="/specific/route" routerLinkActive #rla="routerLinkActive">
  <div *ngIf="!rla.isActive">
    ... show/hide content on a specific route ... 
  </div>
</div>

Upvotes: 0

Simon
Simon

Reputation: 147

After reading through the Angular Router source, I stumbled upon ChildrenOutletContexts which seems to be a map of the currently "active" outlets.

I adjusted my Component to get it injected and check it every time a navigation completes:

import {
  ChildrenOutletContexts,
  NavigationEnd,
  Router
} from "@angular/router";
import {filter} from "rxjs";
import {map} from "rxjs/operators";

@Component({
  selector: 'app-help',
  templateUrl: './help.component.html',
  styleUrls: ['./help.component.scss']
})
export class HelpComponent {
  helpOutletActive$ = this.router.events.pipe(
    filter(event => event instanceof NavigationEnd),
    map(() => this.contexts.getContext('help') !== null)
  )

  constructor(
    private dialog: MatDialog,
    private router: Router,
    private contexts: ChildrenOutletContexts
  ) {
  }

  openHelpCenter() {
    this.dialog.open(HelpDialogComponent, {
      width: '300px'
    })
  }
}

According to my tests this seems to produce the desired result

Upvotes: 1

iamaword
iamaword

Reputation: 1509

You can have a service hold a boolean, and have modules manually flip the help on or off on init to fit their needs. We do something like this in our app, but also have something subscribed to navigation events that flips based on certain routes.

You could also add in a query param into the routes that is something like showhelp=true or false, and react to that in the main component.

You could also have a whitelist in the main component where you hold the help button and listen to routing events here, and just use the mapping to control the ngif on it.

Lots of options, not sure what is the best one, I'm curious what route you end up taking though

Upvotes: 1

Related Questions