264

I have two components as follows and I want to call a function from another component. Both components are included in the third parent component using directive.

Component 1:

@component(
    selector:'com1'
)
export class com1{
    function1(){...}
}

Component 2:

@component(
    selector:'com2'
)
export class com2{
    function2(){...
        // i want to call function 1 from com1 here
    }
}

I've tried using @input and @output but I don't understand exactly how to use it and how to call that function, can anyone help?

Tushar Walzade
  • 3,737
  • 4
  • 33
  • 56
noobProgrammer
  • 2,884
  • 3
  • 17
  • 20

11 Answers11

433

First, what you need to understand the relationships between components. Then you can choose the right method of communication. I will try to explain all the methods that I know and use in my practice for communication between components.

What kinds of relationships between components can there be?

1. Parent > Child

enter image description here

Sharing Data via Input

This is probably the most common method of sharing data. It works by using the @Input() decorator to allow data to be passed via the template.

parent.component.ts

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

@Component({
  selector: 'parent-component',
  template: `
    <child-component [childProperty]="parentProperty"></child-component>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent{
  parentProperty = "I come from parent"
  constructor() { }
}

child.component.ts

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

@Component({
  selector: 'child-component',
  template: `
      Hi {{ childProperty }}
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  @Input() childProperty: string;

  constructor() { }

}

This is a very simple method. It is easy to use. We can also catch changes to the data in the child component using ngOnChanges.

But do not forget that if we use an object as data and change the parameters of this object, the reference to it will not change. Therefore, if we want to receive a modified object in a child component, it must be immutable.

2. Child > Parent

enter image description here

Sharing Data via ViewChild

ViewChild allows one component to be injected into another, giving the parent access to its attributes and functions. One caveat, however, is that child won’t be available until after the view has been initialized. This means we need to implement the AfterViewInit lifecycle hook to receive the data from the child.

parent.component.ts

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from "../child/child.component";

@Component({
  selector: 'parent-component',
  template: `
    Message: {{ message }}
    <child-compnent></child-compnent>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements AfterViewInit {

  @ViewChild(ChildComponent) child;

  constructor() { }

  message:string;

  ngAfterViewInit() {
    this.message = this.child.message
  }
}

child.component.ts

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

@Component({
  selector: 'child-component',
  template: `
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  message = 'Hello!';

  constructor() { }

}

Sharing Data via Output() and EventEmitter

Another way to share data is to emit data from the child, which can be listed by the parent. This approach is ideal when you want to share data changes that occur on things like button clicks, form entries, and other user events.

parent.component.ts

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

@Component({
  selector: 'parent-component',
  template: `
    Message: {{message}}
    <child-component (messageEvent)="receiveMessage($event)"></child-component>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent {

  constructor() { }

  message:string;

  receiveMessage($event) {
    this.message = $event
  }
}

child.component.ts

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

@Component({
  selector: 'child-component',
  template: `
      <button (click)="sendMessage()">Send Message</button>
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  message: string = "Hello!"

  @Output() messageEvent = new EventEmitter<string>();

  constructor() { }

  sendMessage() {
    this.messageEvent.emit(this.message)
  }
}

3. Siblings

enter image description here

Child > Parent > Child

I try to explain other ways to communicate between siblings below. But you could already understand one of the ways of understanding the above methods.

parent.component.ts

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

@Component({
  selector: 'parent-component',
  template: `
    Message: {{message}}
    <child-one-component (messageEvent)="receiveMessage($event)"></child1-component>
    <child-two-component [childMessage]="message"></child2-component>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent {

  constructor() { }

  message: string;

  receiveMessage($event) {
    this.message = $event
  }
}

child-one.component.ts

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

@Component({
  selector: 'child-one-component',
  template: `
      <button (click)="sendMessage()">Send Message</button>
  `,
  styleUrls: ['./child-one.component.css']
})
export class ChildOneComponent {

  message: string = "Hello!"

  @Output() messageEvent = new EventEmitter<string>();

  constructor() { }

  sendMessage() {
    this.messageEvent.emit(this.message)
  }
}

child-two.component.ts

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

@Component({
  selector: 'child-two-component',
  template: `
       {{ message }}
  `,
  styleUrls: ['./child-two.component.css']
})
export class ChildTwoComponent {

  @Input() childMessage: string;

  constructor() { }

}

4. Unrelated Components

enter image description here

All the methods that I have described below can be used for all the above options for the relationship between the components. But each has its own advantages and disadvantages.

Sharing Data with a Service

When passing data between components that lack a direct connection, such as siblings, grandchildren, etc, you should be using a shared service. When you have data that should always be in sync, I find the RxJS BehaviorSubject very useful in this situation.

data.service.ts

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

@Injectable()
export class DataService {

  private messageSource = new BehaviorSubject('default message');
  currentMessage = this.messageSource.asObservable();

  constructor() { }

  changeMessage(message: string) {
    this.messageSource.next(message)
  }

}

first.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'first-componennt',
  template: `
    {{message}}
  `,
  styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {

  message:string;

  constructor(private data: DataService) {
      // The approach in Angular 6 is to declare in constructor
      this.data.currentMessage.subscribe(message => this.message = message);
  }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message)
  }

}

second.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'second-component',
  template: `
    {{message}}
    <button (click)="newMessage()">New Message</button>
  `,
  styleUrls: ['./second.component.css']
})
export class SecondComponent implements OnInit {

  message:string;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message)
  }

  newMessage() {
    this.data.changeMessage("Hello from Second Component")
  }

}

Sharing Data with a Route

Sometimes you need not only pass simple data between component but save some state of the page. For example, we want to save some filter in the online market and then copy this link and send to a friend. And we expect it to open the page in the same state as us. The first, and probably the quickest, way to do this would be to use query parameters.

Query parameters look more along the lines of /people?id= where id can equal anything and you can have as many parameters as you want. The query parameters would be separated by the ampersand character.

When working with query parameters, you don’t need to define them in your routes file, and they can be named parameters. For example, take the following code:

page1.component.ts

import {Component} from "@angular/core";
import {Router, NavigationExtras} from "@angular/router";

@Component({
    selector: "page1",
  template: `
    <button (click)="onTap()">Navigate to page2</button>
  `,
})
export class Page1Component {

    public constructor(private router: Router) { }

    public onTap() {
        let navigationExtras: NavigationExtras = {
            queryParams: {
                "firstname": "Nic",
                "lastname": "Raboy"
            }
        };
        this.router.navigate(["page2"], navigationExtras);
    }

}

In the receiving page, you would receive these query parameters like the following:

page2.component.ts

import {Component} from "@angular/core";
import {ActivatedRoute} from "@angular/router";

@Component({
    selector: "page2",
    template: `
         <span>{{firstname}}</span>
         <span>{{lastname}}</span>
      `,
})
export class Page2Component {

    firstname: string;
    lastname: string;

    public constructor(private route: ActivatedRoute) {
        this.route.queryParams.subscribe(params => {
            this.firstname = params["firstname"];
            this.lastname = params["lastname"];
        });
    }

}

NgRx

The last way, which is more complicated but more powerful, is to use NgRx. This library is not for data sharing; it is a powerful state management library. I can't in a short example explain how to use it, but you can go to the official site and read the documentation about it.

To me, NgRx Store solves multiple issues. For example, when you have to deal with observables and when responsibility for some observable data is shared between different components, the store actions and reducer ensure that data modifications will always be performed "the right way".

It also provides a reliable solution for HTTP requests caching. You will be able to store the requests and their responses so that you can verify that the request you're making does not have a stored response yet.

You can read about NgRx and understand whether you need it in your app or not:

Finally, I want to say that before choosing some of the methods for sharing data you need to understand how this data will be used in the future. I mean maybe just now you can use just an @Input decorator for sharing a username and surname. Then you will add a new component or new module (for example, an admin panel) which needs more information about the user. This means that may be a better way to use a service for user data or some other way to share data. You need to think about it more before you start implementing data sharing.

JasperZelf
  • 2,731
  • 1
  • 22
  • 34
Roman Skydan
  • 5,478
  • 4
  • 19
  • 39
  • 1
    in 3. Siblings the ts child-two.component.ts display name in html should be childMessage (for the case use html file) – Nam Nguyễn May 18 '20 at 07:45
  • is there a reference for this comment " The approach in Angular 6 is to declare in constructor" and why it is recommended rather than moving it into `NgOnInit` – Sh eldeeb Jun 01 '23 at 13:49
164

If com1 and com2 are siblings you can use

@component({
  selector:'com1',
})
export class com1{
  function1(){...}
}

com2 emits an event using an EventEmitter

@component({
  selector:'com2',
  template: `<button (click)="function2()">click</button>`
)
export class com2{
  @Output() myEvent = new EventEmitter();
  function2(){...
    this.myEvent.emit(null)
  }
}

Here the parent component adds an event binding to listen to myEvent events and then calls com1.function1() when such an event happens. #com1 is a template variable that allows to refer to this element from elsewhere in the template. We use this to make function1() the event handler for myEvent of com2:

@component({
  selector:'parent',
  template: `<com1 #com1></com1><com2 (myEvent)="com1.function1()"></com2>`
)
export class com2{
}

For other options to communicate between components see also component-interaction

Muhammed Albarmavi
  • 23,240
  • 8
  • 66
  • 91
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Your question doesn't contain anything about parent-child. What do you try to accomplish? – Günter Zöchbauer Jun 02 '16 at 10:02
  • when you started saying "Here the parent component adds an event binding to listen to myEvent", I'm very confused. I thought we are trying to solve the sibling component situation. The angular link is all parent child, so I can't use those. – Angela P Jun 27 '17 at 13:57
  • It's the parent of rhe siblings (you can also say host). `` is an event binding for the parent/host, because that's the only way Angular provides. The event handler however calls a method on the other sibling (`com1`). The parent is used as a mediator. – Günter Zöchbauer Jun 27 '17 at 18:20
  • How to call it out of the view?! Just inside the `somecomponent.ts`? – Mohammad Kermani Jul 01 '17 at 08:41
  • But if two different component(On Click event((one component)) for some user in list & copy that click event on detail page(another component) ) - From One Component method I want to use in another component, how to do that Please tell me ??? @GünterZöchbauer – Jignesh Vagh Jul 04 '17 at 12:03
  • @JigneshVagh sorry, but I don't understand what you try to accomplish. I'd suggest you create a new question with your code that demonstrates what what you try to accomplish. – Günter Zöchbauer Jul 04 '17 at 12:13
  • And if I need to pass parameters with the event emitter? Something like com1.function(parameter) – Matheus Bernardi May 26 '19 at 21:30
  • @MatheusBernardi you can do that. – Günter Zöchbauer May 27 '19 at 02:56
  • 1
    Good explanation..! – Gajen Dissanayake Sep 03 '21 at 22:59
124

You can access component one's method from component two..

componentOne

  ngOnInit() {}

  public testCall(){
    alert("I am here..");    
  }

componentTwo

import { oneComponent } from '../one.component';


@Component({
  providers:[oneComponent ],
  selector: 'app-two',
  templateUrl: ...
}


constructor(private comp: oneComponent ) { }

public callMe(): void {
    this.comp.testCall();
  }

componentTwo html file

<button (click)="callMe()">click</button>
Liam
  • 27,717
  • 28
  • 128
  • 190
Jayantha
  • 2,189
  • 1
  • 12
  • 13
  • 2
    This was it for me until I needed to for instance access a variable in componentOne from componentTwo by invoking testCall.... the case for which the variable value is set by componentOne but componentTwo will get the default and not the current. – rey_coder Jul 14 '18 at 20:08
  • 2
    I am not getting componentOne variable values inside to the testCall method if i call testCall from componentTwo. Any idea? – Raja Oct 08 '18 at 04:01
  • Please explain this method for Ionic 4 with Angular 7 – Mohammad Ayoub Khan May 05 '19 at 11:27
  • with this way it does not triger @Raja having the same problem – Kevin Dias Aug 16 '19 at 17:46
  • 1
    This is worst way to solve problem because of these reasons : (1) It causes circular dependency problem every time you run npm run build/start (2) You need to provide providers in component level (3) if you are doing data based operation in different components by sharing methods, this way is not going to work. Alternatives : use event emitter to pass data between two components and handle from methods appropriately and if its possible share method from a service. – surendrapanday Dec 12 '19 at 07:49
  • 1
    Simply injected it in and call it worked well for me. – stoneshishang Oct 28 '22 at 14:46
49

Component 1(child):

@Component(
  selector:'com1'
)
export class Component1{
  function1(){...}
}

Component 2(parent):

@Component(
  selector:'com2',
  template: `<com1 #component1></com1>`
)
export class Component2{
  @ViewChild("component1") component1: Component1;

  function2(){
    this.component1.function1();
  }
}
Liam
  • 27,717
  • 28
  • 128
  • 190
Ihor Khomiak
  • 1,151
  • 11
  • 17
  • Good answer, see also [here](https://angular.io/guide/component-interaction#parent-calls-an-viewchild). – redevined Feb 10 '18 at 10:50
  • 3
    all right how i call function2 on html? i always got this.component1 is undefined – cajuuh Feb 22 '18 at 21:11
  • 1
    This worked for me, once i realized that you had to import ViewChild from @angular/core and also Component1 from wherever it is. – Dallas Caley Mar 16 '18 at 17:27
  • @IhorKhomiak I have used the same solution for one of my problems but I see the error TS2339: Property 'X' does not exist on type 'Y', 'X' being the method defined in the component 'Y'. Please suggest a solution. – Rupam Datta Jun 19 '18 at 04:52
  • I guess you need to check row with @ViewChild(...). There should be the exact type of your component: @ViewChild("component1") component1: Component1; And not ElementRef type. Maybe it will help you. – Ihor Khomiak Jun 19 '18 at 08:28
  • 1
    Instead of extending the component class, i uesd `@ViewChild` which worked for me. Thanks for this example. – Yash Jul 17 '18 at 13:38
29

It depends on the relation between your components (parent / child) but the best / generic way to make communicate components is to use a shared service.

See this doc for more details:

That being said, you could use the following to provide an instance of the com1 into com2:

<div>
  <com1 #com1>...</com1>
  <com2 [com1ref]="com1">...</com2>
</div>

In com2, you can use the following:

@Component({
  selector:'com2'
})
export class com2{
  @Input()
  com1ref:com1;

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}
Liam
  • 27,717
  • 28
  • 128
  • 190
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
5

Using Dataservice we can call the function from another component

Component1: The component which we are calling the function

constructor( public bookmarkRoot: dataService ) { } 

onClick(){
     this.bookmarkRoot.callToggle.next( true );
}

dataservice.ts

import { Injectable } from '@angular/core';
@Injectable()
export class dataService {
     callToggle = new Subject();
}

Component2:The component which contains the function

constructor( public bookmarkRoot: dataService ) { 
  this.bookmarkRoot.callToggle.subscribe(( data ) => {
            this.closeDrawer();
        } )
} 

 closeDrawer() {
        console.log("this is called")
    }
Shafeeq Mohammed
  • 1,193
  • 17
  • 25
  • this may call multiple times to avoid that use this code inside constructor `if ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }` – Shafeeq Mohammed Sep 06 '19 at 09:51
  • you may want to use a pipeable operator such as `this.bookmarkRoot.callToggle.pipe(skipWhile(data=>data.length !==0).subscribe( data => this.closeDrawer();)` – super IT guy Mar 15 '21 at 20:02
4

I use trigger fuction1 (child's function) in parent component like this :)

Component 1(child):

@Component(
  selector:'com1'
)
export class Component1{
  function1(){...}
}

Component 2(parent):

@Component(
  selector:'com2',
  template: `<button (click)="component1.function1()"
             <com1 #component1></com1>`
)
export class Component2{
}

#component1 is a template variable. You can replace it with any name. (Ex: #hello1)

Lojith Vinsuka
  • 906
  • 1
  • 10
  • 8
  • I have a single button on a page that has to fire functions on multiple child components at the same time. This feels wrong to do for some reason, but it works if you string the functions together in the click event as multiple statements. Thanks! – PaulBunion Mar 01 '23 at 15:33
2

In the real world, scenario its not about calling a simple function but a function with a proper value. So let's dive in. This is the scenario A user has a requirement to fire an event from his own component and also at the end he wants to call a function of another component as well. Let's say the service file is the same for both the components

componentOne.html

    <button (click)="savePreviousWorkDetail()" data-dismiss="modal" class="btn submit-but" type="button">
          Submit
        </button>

When the user clicks on the submit button he needs to call savePreviousWorkDetail() in its own component componentOne.ts and in the end he needs to call a function of another component as well. So to do that a function in a service class can be called from componentOne.ts and when that called, the function from componentTwo will be fired.

componentOne.ts

constructor(private httpservice: CommonServiceClass) {
  }

savePreviousWorkDetail() {
// Things to be Executed in this function

this.httpservice.callMyMethod("Niroshan");
}

commontServiceClass.ts

import {Injectable,EventEmitter} from '@angular/core';

@Injectable()
export class CommonServiceClass{

  invokeMyMethod = new EventEmitter();

  constructor(private http: HttpClient) {
  }

  callMyMethod(params: any = 'Niroshan') {
    this.invokeMyMethod.emit(params);
  }

}

And Below is the componentTwo which has the function that needed to be called from componentOne. And in ngOnInit() we have to subscribe for the invoked method so when it triggers methodToBeCalled() will be called

componentTwo.ts

import {Observable,Subscription} from 'rxjs';


export class ComponentTwo implements OnInit {

 constructor(private httpservice: CommonServiceClass) {
  }

myMethodSubs: Subscription;

ngOnInit() {
    
    this.myMethodSubs = this.httpservice.invokeMyMethod.subscribe(res => {
      console.log(res);
      this.methodToBeCalled();
    });
    
methodToBeCalled(){
//what needs to done
}
  }

}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Niroshan Ratnayake
  • 3,433
  • 3
  • 20
  • 18
2

For unrelated components use this simple method using a shared service.

//YourService

private subject = new Subject<any>();
sendClickEvent() {
  this.subject.next();
}
getClickEvent(): Observable<any>{ 
  return this.subject.asObservable();
}
}

//Component where you have button

clickMe(){
this.YourServiceObj.sendClickEvent();
}

<button (click)="clickMe()">Click Me</button>

//Componenet where you recieve click event

    this.sharedService.getClickEvent().subscribe(()=>{
    this.doWhateverYouWant();
    }

)
Faisal Mushtaq
  • 485
  • 4
  • 11
1
  • Lets say 1st component is DbstatsMainComponent
  • 2nd component DbstatsGraphComponent.
  • 1st component calling the method of 2nd

<button (click)="dbgraph.displayTableGraph()">Graph</button> <dbstats-graph #dbgraph></dbstats-graph>

Note the local variable #dbgraph on the child component, which the parent can use to access its methods (dbgraph.displayTableGraph()).

Ikhlak S.
  • 8,578
  • 10
  • 57
  • 77
RAHUL KUMAR
  • 1,123
  • 11
  • 9
1
  1. Add injectable decorator to component2(or any component which have the method)
@Injectable({
    providedIn: 'root'
})
  1. Inject into component1 (the component from where component2 method will be called)
constructor(public comp2 : component2) { }
  1. Define method in component1 from where component2 method is called
method1()
{
    this.comp2.method2();
}

component 1 and component 2 code below.

import {Component2} from './Component2';

@Component({
  selector: 'sel-comp1',
  templateUrl: './comp1.html',
  styleUrls: ['./comp1.scss']
})
export class Component1 implements OnInit {
  show = false;
  constructor(public comp2: Component2) { }
method1()
 {
   this.comp2.method2(); 
  }
}


@Component({
  selector: 'sel-comp2',
  templateUrl: './comp2.html',
  styleUrls: ['./comp2.scss']
})
export class Component2 implements OnInit {
  method2()
{
  alert('called comp2 method from comp1');
}
Joundill
  • 6,828
  • 12
  • 36
  • 50
Varma
  • 11
  • 3