Cómo llamar a otra función de componentes en angular2

154

Tengo dos componentes de la siguiente manera y quiero llamar a una función desde otro componente. Ambos componentes se incluyen en el tercer componente principal mediante la directiva.

Componente 1:

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

Componente 2:

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

He intentado usar @inputy @outputpero no entiendo exactamente cómo usarlo y cómo llamar a esa función, ¿alguien puede ayudarme?

noobProgrammer
fuente

Respuestas:

130

Si com1 y com2 son hermanos, puede usar

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

com2 emite un evento utilizando un EventEmitter

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

Aquí el componente principal agrega un enlace de evento para escuchar myEventeventos y luego llama com1.function1()cuando tal evento ocurre. #com1es una variable de plantilla que permite hacer referencia a este elemento desde otra parte de la plantilla. Lo utilizamos para hacer que function1()el controlador de eventos para los myEventde com2:

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

Para otras opciones para comunicarse entre componentes, ver también interacción de componentes

Günter Zöchbauer
fuente
Su pregunta no contiene nada sobre padre-hijo. ¿Qué intentas lograr?
Günter Zöchbauer
cuando comenzaste a decir "Aquí el componente padre agrega un enlace de evento para escuchar myEvent", estoy muy confundido. Pensé que estamos tratando de resolver la situación del componente hermano. El enlace angular es todo padre hijo, por lo que no puedo usarlos.
Angela P
Es el padre de los hermanos (también se puede decir host). <sibling1 (myEvent)="...">es un enlace de evento para el padre / host, porque esa es la única forma en que Angular proporciona. Sin embargo, el controlador de eventos llama a un método en el otro hermano ( com1). El padre se usa como mediador.
Günter Zöchbauer
¿Cómo llamarlo fuera de la vista? Justo dentro de la somecomponent.ts?
Mohammad Kermani
Pero si dos componentes diferentes (evento On Click ((un componente)) para algún usuario en la lista y copian ese evento click en la página de detalles (otro componente)) - Desde el método de un componente que quiero usar en otro componente, cómo hacerlo Por favor dime ??? @ GünterZöchbauer
Jignesh Vagh el
164

Primero, lo que necesita para comprender las relaciones entre los componentes. Entonces puede elegir el método de comunicación correcto. Trataré de explicar todos los métodos que conozco y utilizo en mi práctica para la comunicación entre componentes.

¿Qué tipo de relaciones entre componentes puede haber?

1. Padre> Niño

ingrese la descripción de la imagen aquí

Compartir datos a través de la entrada

Este es probablemente el método más común para compartir datos. Funciona usando el @Input()decorador para permitir que los datos pasen a través de la plantilla.

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() { }

}

Este es un método muy simple. Es fácil de usar. También podemos detectar cambios en los datos en el componente secundario utilizando ngOnChanges .

Pero no olvide que si usamos un objeto como datos y cambiamos los parámetros de este objeto, la referencia a él no cambiará. Por lo tanto, si queremos recibir un objeto modificado en un componente hijo, debe ser inmutable.

2. Niño> Padre

ingrese la descripción de la imagen aquí

Compartir datos a través de ViewChild

ViewChild permite que un componente se inyecte en otro, dando al padre acceso a sus atributos y funciones. Sin embargo, una advertencia es que childno estará disponible hasta después de que se haya inicializado la vista. Esto significa que necesitamos implementar el enlace del ciclo de vida AfterViewInit para recibir los datos del niño.

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() { }

}

Compartir datos a través de Output () y EventEmitter

Otra forma de compartir datos es emitir datos del elemento secundario, que el padre puede enumerar. Este enfoque es ideal cuando desea compartir los cambios de datos que ocurren en cosas como clics de botones, entradas de formularios y otros eventos de usuario.

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. hermanos

ingrese la descripción de la imagen aquí

Niño> Padre> Niño

Trato de explicar otras formas de comunicación entre hermanos a continuación. Pero ya podría entender una de las formas de entender los métodos anteriores.

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. Componentes no relacionados

ingrese la descripción de la imagen aquí

Todos los métodos que he descrito a continuación se pueden usar para todas las opciones anteriores para la relación entre los componentes. Pero cada uno tiene sus propias ventajas y desventajas.

Compartir datos con un servicio

Al pasar datos entre componentes que carecen de una conexión directa, como hermanos, nietos, etc., debe usar un servicio compartido. Cuando tiene datos que siempre deben estar sincronizados, encuentro que RxJS BehaviorSubject es muy útil en esta situación.

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")
  }

}

Compartir datos con una ruta

A veces, no solo necesita pasar datos simples entre componentes, sino también guardar algún estado de la página. Por ejemplo, queremos guardar un filtro en el mercado en línea y luego copiar este enlace y enviarlo a un amigo. Y esperamos que abra la página en el mismo estado que nosotros. La primera forma, y ​​probablemente la más rápida, de hacerlo sería utilizar parámetros de consulta .

Los parámetros de consulta se ven más en la línea de /people?id=dónde idpuede ser igual a cualquier cosa y puede tener tantos parámetros como desee. Los parámetros de consulta estarían separados por el carácter de y comercial.

Cuando trabaje con parámetros de consulta, no necesita definirlos en su archivo de rutas, y pueden denominarse parámetros. Por ejemplo, tome el siguiente código:

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);
    }

}

En la página de recepción, recibiría estos parámetros de consulta como los siguientes:

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

La última forma, que es más complicada pero más poderosa, es usar NgRx . Esta biblioteca no es para compartir datos; Es una poderosa biblioteca de gestión de estado. En un breve ejemplo, no puedo explicar cómo usarlo, pero puedes ir al sitio oficial y leer la documentación al respecto.

Para mí, NgRx Store resuelve múltiples problemas. Por ejemplo, cuando tiene que tratar con observables y cuando la responsabilidad de algunos datos observables se comparte entre diferentes componentes, las acciones de la tienda y el reductor aseguran que las modificaciones de datos siempre se realizarán "de la manera correcta".

También proporciona una solución confiable para el almacenamiento en caché de solicitudes HTTP. Podrá almacenar las solicitudes y sus respuestas para que pueda verificar que la solicitud que está realizando aún no tiene una respuesta almacenada.

Puede leer sobre NgRx y comprender si lo necesita en su aplicación o no:

Finalmente, quiero decir que antes de elegir algunos de los métodos para compartir datos, debe comprender cómo se utilizarán estos datos en el futuro. Quiero decir, quizás ahora puedes usar solo un @Inputdecorador para compartir un nombre de usuario y un apellido. Luego agregará un nuevo componente o un nuevo módulo (por ejemplo, un panel de administración) que necesita más información sobre el usuario. Esto significa que puede ser una mejor manera de utilizar un servicio para los datos del usuario o alguna otra forma de compartir datos. Debe pensarlo más antes de comenzar a implementar el intercambio de datos.

Roman Skydan
fuente
3
mejor explicación con ejemplos
afeef
1
la mejor respuesta a este tema que he visto, felicidades, ..
Solo
Excelente explicación
Moisés Aguilar
en 3. Hermanos, el nombre para mostrar ts child-two.component.ts en html debe ser childMessage (para el caso, use el archivo html)
Nam Nguyễn
84

Puede acceder al método del componente uno desde el componente dos.

componente uno

  ngOnInit() {}

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

componente Dos

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


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


constructor(private comp: oneComponent ) { }

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

componente Dos archivos html

<button (click)="callMe()">click</button>
Jayantha
fuente
2
Esto fue para mí hasta que necesitaba, por ejemplo, acceder a una variable en componentOne desde componentTwo invocando testCall ... el caso para el cual el valor de la variable se establece por componentOne pero componentTwo obtendrá el valor predeterminado y no el actual.
rey_coder
2
No obtengo valores variables de componentOne dentro del método testCall si llamo a testCall desde componentTwo. ¿Alguna idea?
Raja
Explique este método para Ionic 4 con Angular 7
Mohammad Ayoub Khan,
de esta manera, no triunfa @Raja tiene el mismo problema
Kevin Dias
1
THQQQQQQQQQQQ TAN HOMBRE
Ashu
33

Componente 1 (hijo):

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

Componente 2 (padre):

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

  function2(){
    this.component1.function1();
  }
}
Ihor Khomiak
fuente
Buena respuesta, ver también aquí .
Cipher
bien, ¿cómo llamo a function2 en html? siempre tengo this.component1 no está definido
cajuuh
<com1 # component1 (click) = "function2 ()"> </com1>
Ihor Khomiak
1
Esto funcionó para mí, una vez que me di cuenta de que tenía que importar ViewChild desde @ angular / core y también Component1 desde donde sea que esté.
Dallas Caley
1
En lugar de extender la clase de componente, supuse @ViewChildcuál funcionó para mí. Gracias por este ejemplo
Yash
27

Depende de la relación entre sus componentes (padre / hijo), pero la mejor forma genérica de hacer que los componentes de comunicación se comuniquen es usar un servicio compartido.

Vea este documento para más detalles:

Dicho esto, puede usar lo siguiente para proporcionar una instancia de com1 en com2:

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

En com2, puede usar lo siguiente:

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

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}
Thierry Templier
fuente
recibo un error No puedo vincularme a 'com1Ref' ya que no es una propiedad nativa conocida
noobProgrammer
y no está tomando this.com1.function1 (); en su lugar this.com1ref.function1 ();
noobProgrammer
¡Gracias por señalar el error tipográfico! ¿Tienes el @Inputen el campo?
Thierry Templier
ok, está funcionando, ¿puedes decirme cómo lograr lo mismo para la relación padre-hijo?
noobProgrammer
1
He intentado mismo para padres e hijos pero dice function1 () de undegined
noobProgrammer
1
  • Digamos que el primer componente es DbstatsMainComponent
  • 2do componente DbstatsGraphComponent.
  • 1er componente llamando al método del 2do

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

Tenga en cuenta la variable local #dbgraphen el componente secundario, que el padre puede usar para acceder a sus métodos ( dbgraph.displayTableGraph()).

RAHUL KUMAR
fuente
0

Usando Dataservice podemos llamar a la función desde otro componente

Componente1: el componente al que llamamos la función

constructor( public bookmarkRoot: dataService ) { } 

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

dataservice.ts

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

Componente2: el componente que contiene la función

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

 closeDrawer() {
        console.log("this is called")
    }
Shafeeq Mohammed
fuente
esto puede llamar varias veces para evitar que use este código dentro del constructorif ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }
Shafeeq Mohammed