19

This question is quite simple but I can't get rid of it.

I have a <header> in parent template and I need it disappear when displaying children template through routing module. What I expect is adding a class to the header tag so I can hide it via CSS. This is what I have:

app.component.ts

import { Component } from '@angular/core';
@Component({
  selector: 'app',
  template: `
    <header [class.hidden]="hide">
      <h1>My App</h1>
      <ul>
            <li><a href="/home"></a></li>
            <li><a href="/showoff"></a></li>
      </ul>
    </header>
    <router-outlet></router-outlet>
  `
})

export class AppComponent {
  hide = false; // <-- This is what I need to change in child component
}

app-routing.module.ts

import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from './welcome.component';
import { ShowOffComponent } from './show.off.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'showoff', component: ShowOffComponent },
];

export const AppRouting = RouterModule.forRoot(routes, {
  useHash: true
});

show.offf.component.ts

import { Component } from '@angular/core';
@Component({
   selector: 'app-showoff',
   template: `
       <h2>Show Off</h2>
       <div>Some contents...</div>
   `
})

export class ShowOffComponent {
   hide = true; // <-- Here is the problem.
                // I don't have any idea how to reach parent variables.
}
1
  • you can accept answer as correct if it worked for you. Commented Apr 12, 2022 at 10:51

4 Answers 4

20

You can use output emitter

In your child component,

import { Component } from '@angular/core';
@Component({
   selector: 'app-showoff',
   template: `
       <h2>Show Off</h2>
       <div>Some contents...</div>
   `
})

export class ShowOffComponent {
    @Output() onHide = new EventEmitter<boolean>();
    setHide(){
       this.onHide.emit(true);
    }
}

In the parent,

export class AppComponent {
  hide = false;

  changeHide(val: boolean) {
    this.hide = val;
  }
}

Add a child to the parent with that event emitter,

<app-showoff (onHide)="changeHide($event)"></app-showoff>
5
  • and for more complex scenarios you can do a service ('LayoutService') and create there an EventEmitter, so wherever you want you can use this Event for any component. Commented Jan 16, 2017 at 6:53
  • right.. there are plenty of ways of parent child communication as per the docs depending on specific scenarios.
    – Suraj Rao
    Commented Jan 16, 2017 at 6:59
  • Thank you, but the onHide() argument on parent component does nothing in this case. I have tested it using simple alert on both setHide() and onHide(). When (click) handler triggered, only setHide() showing alert. And then I realize this can't be implemented using route module after I found [this] (stackoverflow.com/questions/35878762/…).
    – dkregen
    Commented Jan 16, 2017 at 7:23
  • ok.. so You need to use a common service. here
    – Suraj Rao
    Commented Jan 16, 2017 at 7:26
  • Well, it works! But, to make the service working, it asked me to define <app-showoff [onHide]="hide"></app-showoff> inside router-outlet which is not great, as it will made the template still exist even when I change the route.Besides, using service is the way more complex to achieve this, since I just need to set a variable to true/false, not bi-directional communication.
    – dkregen
    Commented Jan 17, 2017 at 0:58
10

I have bumped into the same phenomena very recently with Angular 6.

I wanted to access and change a variable which is belongs to a parent or a grandparent Component. See the image below. I want to access and change a variable which is belongs to Tom, from Jim (who is a child of Tom) and Sara (who is a grandchild of Tom).

enter image description here

There might be some other solutions but the approach I have used to overcome this is by using ViewContainerRef. I have to inject the ViewContainerRef to the Child component's constructor, that I need to access the parent Component and traversed through the parent node (or nodes) to find the parent variable.

Injecting to the constructor,

constructor(private viewContainerRef: ViewContainerRef) { }

Accessing and traversing through to the parent node,

getParentComponent() {
    return this.viewContainerRef[ '_data' ].componentView.component.viewContainerRef[ '_view' ].component
}

I have made a StackBlitz example with the scenario that I have given you in the above picture.

https://stackblitz.com/edit/angular-comms

Hope someone will find this useful.

Thank you.

3
  • 1
    I just ran into this issue, thanks for the fix! you saved me quite a bit of time! Commented Aug 21, 2018 at 9:12
  • 1
    @JacquesOlivier I am glad you found my answer helpful :) Commented Aug 21, 2018 at 9:37
  • @AnjanaSilva It's not working angular 12. Kindly share here that solution. I have componentView undefined console error. Commented Aug 16, 2021 at 13:09
3

In your app.component.ts you can check your URL and set hide variable value as below :

import { Component } from '@angular/core';
import { Router, NavigationStart } from '@angular/router';

@Component({
  selector: 'app',
  template: `
    <header [class.hidden]="hide">
      <h1>My App</h1>
      <ul>
            <li><a href="/home"></a></li>
            <li><a href="/showoff"></a></li>
      </ul>
    </header>
    <router-outlet></router-outlet>
  `
})

export class AppComponent {
  hide = false; // <-- This is what I need to change in child component

  constructor(private router: Router){}

  public ngOnInit() {
    this.router.events.subscribe((events) => {
      if (events instanceof NavigationStart) {
        if (events.url === '/' || events.url === '/home') {
          this.hide = false;
        } else {
          this.hide = true;
        }
      }
    });
  }

}
0
0

Less straightforward, but (imho) more accurate way to achieve this is to use service.

  1. create parent-to-child.service.ts file and create a subject property

    export class ParentToChildService { showHeader = new Subject(); }

  2. inject it via a constructor in both parent and child components:

    constructor(private ptcService: ParentToChildService ) {}

  3. Bind the header component with ngIf (or you can use ngClass, if you insist on using CSS approach) to the property in its TS file:

html: <header *ngIf="showStatus">
TS: showStatus: boolean = true; // true by default ptcSubscription: Subscription;

  1. On ngInit of the header component subscribe to that subject:

    ngOnInit(): { this.ptcSubscription = this.ptcService.showHeader.subscribe( (newStatus: boolean) => { this.showStatus = newStatus }); } ); }

  2. In your child component, whenever you need to hide the <header>, call next on the service subject:

    this.ptcService.showHeader.next(false); //hiding the header

Not the answer you're looking for? Browse other questions tagged or ask your own question.