Como chamar outra função de componentes em angular2

154

Eu tenho dois componentes da seguinte maneira e quero chamar uma função de outro componente. Ambos os componentes estão incluídos no terceiro componente pai usando a diretiva.

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

Eu tentei usar @inpute, @outputmas não entendo exatamente como usá-lo e como chamar essa função, alguém pode ajudar?

noobProgrammer
fonte

Respostas:

130

Se com1 e com2 são irmãos, você pode usar

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

com2 emite um evento usando um EventEmitter

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

Aqui, o componente pai adiciona uma ligação de evento para escutar myEventeventos e depois chama com1.function1()quando esse evento acontece. #com1é uma variável de modelo que permite se referir a esse elemento de qualquer outra parte do modelo. Nós usamos isso para fazer function1()o manipulador de eventos para myEventde com2:

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

Para outras opções de comunicação entre componentes, consulte também interação com componentes

Günter Zöchbauer
fonte
Sua pergunta não contém nada sobre pai e filho. O que você tenta realizar?
Günter Zöchbauer
quando você começou a dizer "Aqui o componente pai adiciona uma ligação de evento para ouvir myEvent", estou muito confuso. Eu pensei que estávamos tentando resolver a situação do componente irmão. O link angular é todo filho pai, então não posso usá-los.
Angela P
É o pai dos irmãos (você também pode dizer anfitrião). <sibling1 (myEvent)="...">é uma ligação de evento para o pai / host, porque é a única maneira que o Angular fornece. O manipulador de eventos, no entanto, chama um método no outro irmão ( com1). O pai é usado como mediador.
Günter Zöchbauer
Como tirar isso de vista ?! Apenas dentro do somecomponent.ts?
Mohammad Kermani
Mas se dois componentes diferentes (evento On Click ((um componente)) para algum usuário na lista e copiar esse evento click na página de detalhes (outro componente)) - De um método de componente que eu quero usar em outro componente, como fazer isso Por favor, diga ??? @ GünterZöchbauer
Jignesh Vagh
164

Primeiro, o que você precisa para entender os relacionamentos entre os componentes. Então você pode escolher o método certo de comunicação. Vou tentar explicar todos os métodos que eu conheço e uso na minha prática para a comunicação entre componentes.

Que tipos de relacionamentos entre componentes podem existir?

1. Pai> Filho

insira a descrição da imagem aqui

Compartilhando dados via entrada

Este é provavelmente o método mais comum de compartilhamento de dados. Ele funciona usando o @Input()decorador para permitir que os dados sejam transmitidos pelo modelo.

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 é um método muito simples. É fácil de usar. Também podemos capturar alterações nos dados no componente filho usando ngOnChanges .

Mas não esqueça que se usarmos um objeto como dados e alterarmos os parâmetros desse objeto, a referência a ele não será alterada. Portanto, se queremos receber um objeto modificado em um componente filho, ele deve ser imutável.

2. Criança> Pai

insira a descrição da imagem aqui

Compartilhando dados via ViewChild

O ViewChild permite que um componente seja injetado em outro, dando ao pai acesso a seus atributos e funções. Uma ressalva, no entanto, é que childnão estará disponível até que a visualização tenha sido inicializada. Isso significa que precisamos implementar o gancho do ciclo de vida AfterViewInit para receber os dados do filho.

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

}

Compartilhando dados via Output () e EventEmitter

Outra maneira de compartilhar dados é emitir dados do filho, que podem ser listados pelos pais. Essa abordagem é ideal quando você deseja compartilhar alterações de dados que ocorram em itens como cliques em botões, entradas de formulário e outros eventos do usuário.

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. irmãos

insira a descrição da imagem aqui

Criança> Pai> Criança

Eu tento explicar outras maneiras de se comunicar entre irmãos abaixo. Mas você já pode entender uma das maneiras de entender os métodos acima.

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 não relacionados

insira a descrição da imagem aqui

Todos os métodos que descrevi abaixo podem ser usados ​​para todas as opções acima para o relacionamento entre os componentes. Mas cada um tem suas próprias vantagens e desvantagens.

Compartilhando dados com um serviço

Ao transmitir dados entre componentes que não possuem conexão direta, como irmãos, netos, etc., você deve usar um serviço compartilhado. Quando você tem dados que sempre devem estar sincronizados, acho o RxJS BehaviorSubject muito útil nessa situação.

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

}

Compartilhando dados com uma rota

Às vezes, você precisa não apenas passar dados simples entre os componentes, mas salvar algum estado da página. Por exemplo, queremos salvar algum filtro no mercado on-line, copiar esse link e enviar para um amigo. E esperamos que ela abra a página no mesmo estado que nós. A primeira, e provavelmente a mais rápida, maneira de fazer isso seria usar parâmetros de consulta .

Os parâmetros de consulta são mais parecidos com /people?id=onde idpode ser qualquer coisa e você pode ter quantos parâmetros desejar. Os parâmetros da consulta seriam separados pelo caractere e comercial.

Ao trabalhar com parâmetros de consulta, você não precisa defini-los no arquivo de rotas e eles podem ser nomeados parâmetros. Por exemplo, pegue o seguinte 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);
    }

}

Na página de recebimento, você receberá esses parâmetros de consulta como os seguintes:

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

A última maneira, que é mais complicada, mas mais poderosa, é usar o NgRx . Esta biblioteca não é para compartilhamento de dados; é uma poderosa biblioteca de gerenciamento de estado. Em um breve exemplo, não posso explicar como usá-lo, mas você pode ir ao site oficial e ler a documentação sobre ele.

Para mim, a NgRx Store resolve vários problemas. Por exemplo, quando você precisa lidar com observáveis ​​e quando a responsabilidade por alguns dados observáveis ​​é compartilhada entre diferentes componentes, as ações da loja e o redutor garantem que as modificações dos dados sempre sejam executadas "da maneira certa".

Ele também fornece uma solução confiável para o cache de solicitações HTTP. Você poderá armazenar os pedidos e suas respostas, para poder verificar se o pedido que você está fazendo ainda não tem uma resposta armazenada.

Você pode ler sobre o NgRx e entender se precisa ou não no seu aplicativo:

Finalmente, quero dizer que, antes de escolher alguns dos métodos para compartilhar dados, você precisa entender como esses dados serão usados ​​no futuro. Quero dizer, talvez agora você possa usar apenas um @Inputdecorador para compartilhar um nome de usuário e sobrenome. Em seguida, você adicionará um novo componente ou novo módulo (por exemplo, um painel de administração) que precisa de mais informações sobre o usuário. Isso significa que pode ser uma maneira melhor de usar um serviço para dados do usuário ou alguma outra maneira de compartilhar dados. Você precisa pensar mais sobre isso antes de começar a implementar o compartilhamento de dados.

Roman Skydan
fonte
3
melhor explicação com exemplos
afeef 18/06/19
1
a melhor resposta para este tópico que eu já vi, congrats, ..
Solitária
Explicação excelente
Moisés Aguilar
em 3. Irmãos, o nome de exibição ts child-two.component.ts em html deve ser childMessage (para o caso, use o arquivo html)
Nam Nguyễn
84

Você pode acessar o método do componente a partir do componente dois.

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 arquivo html

<button (click)="callMe()">click</button>
Jayantha
fonte
2
Foi isso até que eu precisei, por exemplo, acessar uma variável em componentOne a partir de componentTwo, chamando testCall .... o caso em que o valor da variável é definido por componentOne, mas componentTwo obterá o padrão e não o atual.
211118 rey_coder #
2
Eu não estou recebendo valores de variável componentOne dentro do método testCall se eu chamar testCall de componentTwo. Qualquer ideia?
Raja
Por favor, explique este método para Ionic 4 com Angular 7
Mohammad Ayoub Khan
com esta maneira não triger @Raja tendo o mesmo problema
Kevin Dias
1
THQQQQQQQQQQQ MAN TANTO
Ashu
33

Componente 1 (filho):

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

Componente 2 (pai):

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

  function2(){
    this.component1.function1();
  }
}
Ihor Khomiak
fonte
Boa resposta, veja também aqui .
Cipher
tudo bem como eu chamo function2 em html? Eu sempre tenho this.component1 é indefinido
cajuuh
<com1 # component1 (click) = "function2 ()"> </com1>
Ihor Khomiak,
1
Isso funcionou para mim, uma vez que percebi que você tinha que importar o ViewChild de @ angular / core e também Component1 de onde quer que esteja.
Dallas Caley
1
Em vez de estender a classe de componente, uesd @ViewChildque funcionou para mim. Obrigado por este exemplo.
Yash
27

Depende da relação entre seus componentes (pai / filho), mas a melhor maneira / genérica de criar componentes de comunicação é usar um serviço compartilhado.

Consulte este documento para obter mais detalhes:

Dito isto, você pode usar o seguinte para fornecer uma instância do com1 no com2:

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

No com2, você pode usar o seguinte:

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

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}
Thierry Templier
fonte
Estou recebendo erro Não é possível vincular a 'com1Ref', pois não é uma propriedade nativa conhecida
noobProgrammer
e não está assumindo this.com1.function1 (); em vez disso, this.com1ref.function1 ();
precisa saber é o seguinte
Obrigado por apontar o erro de digitação! Você tem @Inputo campo?
Thierry Templier
ok está funcionando, você pode me dizer como conseguir o mesmo para a relação pai - filho?
precisa saber é o seguinte
1
Eu tentei mesmo para criança do pai, mas ele diz function1 () de undegined
noobProgrammer
1
  • Vamos dizer que o primeiro componente é DbstatsMainComponent
  • Segundo componente DbstatsGraphComponent.
  • 1º componente chamando o método do 2º

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

Observe a variável local #dbgraphno componente filho, que o pai pode usar para acessar seus métodos ( dbgraph.displayTableGraph()).

RAHUL KUMAR
fonte
0

Usando o Dataservice, podemos chamar a função de outro componente

Componente1: O componente que estamos chamando de função

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: O componente que contém a função

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

 closeDrawer() {
        console.log("this is called")
    }
Shafeeq Mohammed
fonte
isso pode chamar várias vezes para evitar que o uso deste código dentro do construtorif ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }
Shafeeq Mohammed