1

since I can't find any straight option maybe someone will help me here. The problem is that I have Angular project with following structure:

<app-navigation></app-navigation>

<router-outlet></router-outlet>

<app-footer></app-footer>

and there is a case, where only one component shouldn't have navigation bar visible. Is it possible to hide <app-navigation> from within <router-outlet> component?

Lacwik
  • 57
  • 1
  • 8

2 Answers2

4

You could create a shared service that is injected into both the app component and any other pages that need to hide the navigation component.

Example of a shared service that exposes an observable toggle:

display.service.ts

@Injectable({
  providedIn: 'root'
})
export class DisplayService {
  
  public constructor() {
    this.showNavigation$ = this.showNavigation.asObservable();
  }
  public showNavigation$: Observable<boolean>;

  private showNavigation: Subject<boolean> = new Subject<boolean>();  

  public setNavigationVisibility(visible: boolean): void {
    this.showNavigation.next(visible);
  }
}

This services exposes an observable that can be subscribed to, and a method for emitting values via the observable.

The components that you want to hide the navigation bar in would hide the navigation bar in ngOnInit and show it again in ngOnDestroy.

component.ts

@Component({
  selector: 'app-hide-navigation',
  template: `<p>COMPONENT</p>`,
})
export class HideNavigationComponent implements OnInit, OnDestroy {

  constructor(private displayService: DisplayService) {
  }

  ngOnInit(): void {
    this.displayService.setNavigationVisibility(false);
  }

  ngOnDestroy(): void {
    this.displayService.setNavigationVisibility(true);
  }
}

Your app component could then subscribe to the observable and set the visibility accordingly:

app.component.ts

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  constructor(private displayService: DisplayService) {}

  showNavigation = true;

  private destroyed: Subject<void> = new Subject<void>();

  ngOnInit(): void {
    this.displayService.showNavigation$
      .pipe(takeUntil(this.destroyed))
      .subscribe((visible: boolean) => {
        this.showNavigation = visible;
      });
  }

  ngOnDestroy(): void {
    this.destroyed.next();
  }
}

app.component.html

<app-navigation *ngIf="showNavigation"></app-navigation>
<router-outlet></router-outlet>

The benefit of using an observable like this is that you can easily combine this with an OnPush change detection strategy rather than directly binding to a public boolean property on the service.

Stackblitz demo: https://stackblitz.com/edit/angular-ivy-dk5gi8?file=src/app/app.component.html

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
  • like a charm, thanks a lot! – Lacwik Oct 29 '21 at 15:03
  • Sorry to ask a follow up question after 1.5 years. I have one question regarding the unsubcription of observable. As per my understanding, once angular loads the application, the app component never get destroyed until we close the application. This basically means, the ngOnDestroy method of app component will not get called. In this case, how the subscription is getting unsubscribed? – Ane Jun 09 '23 at 03:47
  • @Ane in the example in my answer the unsubscribe is redundant. As you point out, the app component only gets destroyed when the app instance ends. Looking back on this answer, it simply demonstrates good behaviour when dealing with observables rather than being strictly necessary. Closing the app is a brutal yet equally effective way of closing open subscriptions - if there is no open app then there is no chance of memory leak! – Kurt Hamilton Jun 10 '23 at 05:59
  • Thanks @KurtHamilton for the response. It helps. – Ane Jun 11 '23 at 13:31
1

You can try something like this:

Add a condition to the app-navigation tag, on when should it be displayed. The value can be from any service.

<app-navigation *ngIf="appNavigationService.show"></app-navigation>

And, in the component from which you don't want to show the navigation bar, within the <router-outlet> you can set the value of that field in service class as false. And once you go out of the component, probably in ngOnDestroy you can set it back to true. And also, have the value as true by default.

Arutsudar Arut
  • 195
  • 1
  • 13
  • works fine, but it giving me an error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. – Lacwik Oct 29 '21 at 14:58
  • @Lacwik In that case, you can try `[hidden]`, instead of `*ngIf`. Refer to some more possible solutions for this error `ExpressionChangedAfterItHasBeenCheckedError`, in this [post](https://stackoverflow.com/questions/43375532/expressionchangedafterithasbeencheckederror-explained). Also, the answer posted by `@kurt Hamilton` is more descriptive. Try that out as well. – Arutsudar Arut Oct 29 '21 at 15:09