0

I'm trying to call a method toggleSidebar() of the SidebarComponent from the HeaderComponent trough the method callToggleSidebarOnToggleSidebarBtn() but I get some error that I don't understand. How to use method of a component from another ?

I import the SidebarComponent in the HeaderComponent; In the HeaderComponent, I add the SidebarComponent in the constructor to use one of her methods. I tried to remove the method callToggleSidebarOnToggleSidebarBtn() of header.component.ts but I get the same output.

sidebar.component.html :

<div id="sidebar" class="bg-gray">
<a href="" class="nav-link"><i class="fa fa-calendar"></i> Event</a>
<a href="" class="nav-link"><i class="fa fa-user-circle-o"></i> Profile</a>
<a href="" class="nav-link"><i class="fa fa-commenting-o"></i> Chat</a>
<a href="" class="nav-link"><i class="fa fa-gear"></i> Setting</a>
<a href="" class="nav-link"><i class="fa fa-question-circle-o"></i> Help</a>
<a href="" class="nav-link"><i class="fa fa-sign-out"></i> Log out</a>

sidebar.component.ts :

import { Component, OnInit } from '@angular/core';
import * as $ from 'jquery';

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

  constructor() { }

  ngOnInit() {
  }

  // Display the sidebar if it's not visible and otherwise if else.
  toggleSidebar() {

    $("#sidebar").animate({width: 'toggle'});
  }
}

header.component.html :

<div>
  <nav class="navbar navbar-expand-lg d-flex justify-content-between fixed-top">
    <button class="btn" (click)="callToggleSidebarOnToggleSidebarBtn()"><i class="fa fa-reorder"></i></button>
    <div class="d-flex p-0 m-0">
      <a href="#" class="nav-link d-none d-lg-block">Event</a>
      <a href="#" class="nav-link d-none d-lg-block">Contact Us</a>
      <a href="#" class="nav-link d-none d-lg-block">About Us</a>
      <a href="#" class="nav-link d-none d-lg-block"><i class="fa fa-user-circle"></i></a>
    </div>
    <div class="d-flex">
      <a href="#" class="nav-link"><i class="fa fa-bell"></i></a>
      <a href="#" class="nav-link"><i class="fa fa-globe"></i></a>
    </div>
  </nav>
  <div class="d-sm-flex align-items-center justify-content-between" id="secondMenu">
    <div id="logoContainer">
      <img src="../assets/images/logo.png" alt="" id="logo">
    </div>
    <div class="input-group">
      <div class="input-group-prepend">
        <button class="input-group-text btn dropdown-toggle" data-toggle="dropdown">Meeting</button>
        <div class="dropdown-menu">
          <a class="dropdown-item" class="dropdown-item">Action</a>
          <a class="dropdown-item" class="dropdown-item">Another action</a>
          <a class="dropdown-item" class="dropdown-item">Something else here</a>
        </div>
      </div>
      <input type="text" class="form-control" placeholder="What are you looking for ?" aria-label="Search">
      <div class="input-group-append">
        <button class="btn" type="submit"><i class="fa fa-search"></i></button>
      </div>
    </div>
  </div>
<div>

header.component.ts :

import { Component, OnInit } from '@angular/core';
import { SidebarComponent } from '../sidebar/sidebar.component';

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

  constructor(private sidebarComponent : SidebarComponent) { }

  ngOnInit() {
  }

  callToggleSidebarOnToggleSidebarBtn() {

    this.sidebarComponent.toggleSidebar();
  }
}

I expected to be able to toggle the content of sidebar.component.html by clicking on the button which is at the top left of the header but the actual output is :

ERROR Error: Uncaught (in promise): Error: StaticInjectorError(AppModule) 
[HeaderComponent -> SidebarComponent]: 
  StaticInjectorError(Platform: core)[HeaderComponent -> SidebarComponent]: 
    NullInjectorError: No provider for SidebarComponent!
Error: StaticInjectorError(AppModule)[HeaderComponent -> SidebarComponent]: 
  StaticInjectorError(Platform: core)[HeaderComponent -> SidebarComponent]: 
    NullInjectorError: No provider for SidebarComponent!
    at NullInjector.push../node_modules/@angular/core/fesm5/core.js.NullInjector.get (core.js:3228)
at resolveToken (core.js:3473)
at tryResolveToken (core.js:3417)
at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:3314)
at resolveToken (core.js:3473)
at tryResolveToken (core.js:3417)
at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:3314)
at resolveNgModuleDep (core.js:19784)
at NgModuleRef_.push../node_modules/@angular/core/fesm5/core.js.NgModuleRef_.get (core.js:20473)
at resolveDep (core.js:20844)
at resolvePromise (zone.js:831)
at resolvePromise (zone.js:788)
at zone.js:892
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)
at Object.onInvokeTask (core.js:16147)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:422)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195)
at drainMicroTaskQueue (zone.js:601)
luizzyng
  • 3
  • 3

4 Answers4

1

Components aren't dependencies, they can't be injected.

To do what you want, find the sidebar in your header and give it a reference :

<app-sidebar #sidebarReference></app-sidebar>

You can now either use it in your template :

<button (click)="sidebarReference.toggleSidebar()">...</button>

Or in your component :

@ViewChild(SidebarComponent) sidebar: SidebarComponent;
// or, if several sidebars are in the template,
@ViewChild('sidebarReference') sidebar: SidebarComponent;
this.sidebar.toggleSidebar();
  • The thing is that I try to get this method from header.component.ts. But sidebar.component.html have not to be visible in the header. But in another component. – luizzyng Feb 15 '19 at 17:07
0

Instead of creating an instance of sidebar component in Header component, create a service viz, appService.ts using following command,

ng generate service app

In that service write the below code

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AppService {

  flagChange:Subject<boolean> = new Subject<boolean>();

  constructor() { }

  setFlag(flagValue){
    this.flagChange.next(flagValue);
  }
}

Then in our header.component.ts inject that service instance as

import { Component, OnInit } from '@angular/core';
import { AppService } from './app.service';

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

   flag:boolean = false;

  constructor(private appService :  AppService) { }

  ngOnInit() {
  }

  callToggleSidebarOnToggleSidebarBtn() {

    this.appService.setFlag(!this.flag);
  }
}

Then in our sidebar.component.ts, update code as below,

import { Component, OnInit } from '@angular/core';
import * as $ from 'jquery';
import { AppService } from './app.service';

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

  constructor(private appService: AppService) { }

  ngOnInit() {
   this.appService.flagChange.subscribe((flag) => {
      $("#sidebar").animate({width: 'toggle'});
    })
  }
}

So when the user clicks on icon, flag value in header component gets toggled, this change is registered/given to flagChange variable in AppService using next() function and this change is observed using subscribe method in sidebar component.

0

To solve this problem, I remove first the importation of the sidebar in header.component.ts and the constructor. This is my final header.component.ts :

import { Component, OnInit } from '@angular/core';

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

  constructor() { }

  ngOnInit() {
  }
}

You can see all the changes that I made.

Then I add the following code in the header to reference the SidebarComponent :

<app-sidebar #sidebarReference></app-sidebar>

You can now either use it in your template in header.component.ts

<button class="btn" (click)="sidebarReference.toggleSidebar()"><i class="fa fa-reorder"></i></button>
luizzyng
  • 3
  • 3
  • Why are you posting this as answer, since this is exactly what trichetriche answered you ;) – AT82 Feb 15 '19 at 18:19
  • Sorry sir but before to post this as answer, I validate her own. And I add something in my description. – luizzyng Feb 16 '19 at 19:23
-1

AFAIK, you can't inject component into another component. If you want inter component communication you either : 1. Use a service 2. Or use @Input or @Output construct depending on the relationship between the components.