Search code examples
angulartypescriptangular2-components

Angular 2 call method in nested component


I've found some older answers on this, but the methods they use are no longer valid. I have a header component

import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx'; 

import { AuthenticationService } from '../_services/Authentication.Service';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  loading = false;
  error = '';
  isLoggedIn = false;
  showMessageWindow = false;
  messageType: string = "";
  messageText: string = "";
  public currentUser: any = null;

  constructor(private _auth: AuthenticationService, private router: Router) { }

  ngOnInit() {
    if(this._auth.isLoggedIn()) {
      this.isLoggedIn = true;
    }
    else {
      this._auth.logout();
      this.isLoggedIn = false;
      this.router.navigate(['/']);
    }
  }

  showMessage(message: string, type: string) {
    this.messageText = message;
    this.messageType = type;
    this.showMessageWindow = true;
  }
}

It's pretty basic, shows and manages what navigation can be seen depending on if and who is logged in. I have a warning/alert built into the header component. Not all pages use the header, so I am importing it into the components that do use it and putting it into the component template by putting <app-header></app-header> at the top.

Here is a component that uses the header.
import { Component, OnInit } from '@angular/core';
import { HeaderComponent } from '../header/Header.Component';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-census',
  templateUrl: './census.component.html',
  styleUrls: ['./census.component.css']
})
export class CensusComponent implements OnInit {

  constructor( private router: Router, private activatedRoute: ActivatedRoute) { }

  ngOnInit() {

  }

}

I want to be able to put a method into this component that makes a call to showMessage() from the header component. I've tried a few different ways with minimal success. My biggest question is how do I reference the embedded component. I looked through the documentation, but they always use a template right into the component and don't use a separate html file.

If I try adding

  @ViewChild(HeaderComponent)
  private header: HeaderComponent;

into my CensusComponent I get a warning:

WARNING in ./src/app/header/Header.Component.ts
There are multiple modules with names that only differ in casing.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:

Solution

  • Use the ViewChild decorator factory

    // Here is a component that uses the header.
    
    import {ViewChild} from '@angular/core';
    import {Component, OnInit} from '@angular/core';
    import {HeaderComponent} from '../header/Header.Component';
    import {Router, ActivatedRoute} from '@angular/router';
    
    @Component({
      selector: 'app-census',
      templateUrl: './census.component.html',
      styleUrls: ['./census.component.css']
    })
    export class CensusComponent implements OnInit {
    
      // The argument `HeaderComponent` tells the framework to bind to the child 
      // component whose constructor function is `HeaderComponent`
      @ViewChild(HeaderComponent) header: HeaderComponent;
    
      constructor(readonly router: Router, readonly activatedRoute: ActivatedRoute) {}
    
      ngOnInit() {
        this.header.showMessage('an error occurred!', 'error');
      }
    }
    

    The error you receive:

    WARNING in ./src/app/header/Header.Component.ts
    There are multiple modules with names that only differ in casing.
    This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
    Use equal casing. Compare these module identifiers:
    

    is completely orthogonal.

    Specifically, it means that when you imported header/Header.component to register it in your NgModule, you imported the component using a module specifier with different casing. E.g. header/header.component

    This is a problem that you can and should fix. Just make sure all imports use the same case.

    I recommend using lowercase file names and lowercase module specifiers everywhere. A simple search and replace should take care of it.