Come chiamare un'altra funzione dei componenti in angular2


156

Ho due componenti come segue e voglio chiamare una funzione da un altro componente. Entrambi i componenti sono inclusi nel terzo componente padre usando la direttiva.

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

Ho provato a usare @inpute @outputma non capisco esattamente come usarlo e come chiamare quella funzione, qualcuno può aiutare?


Risposte:


130

Se com1 e com2 sono fratelli puoi usare

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

com2 emette un evento usando un EventEmitter

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

Qui il componente padre aggiunge un binding di eventi per ascoltare gli myEventeventi e quindi chiama com1.function1()quando si verifica un evento del genere. #com1è una variabile modello che consente di fare riferimento a questo elemento da un'altra parte del modello. Usiamo questo per fare function1()il gestore di eventi per myEventdi com2:

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

Per altre opzioni di comunicazione tra i componenti, consultare anche l' interazione tra i componenti


La tua domanda non contiene nulla sul genitore-figlio. Cosa cerchi di realizzare?
Günter Zöchbauer,

quando hai iniziato a dire "Qui il componente genitore aggiunge un'associazione di eventi per ascoltare myEvent", sono molto confuso. Pensavo che stessimo cercando di risolvere la situazione dei componenti fratelli. Il collegamento angolare è tutto figlio genitore, quindi non posso usarli.
Angela P,

È il genitore dei fratelli (puoi anche dire ospite). <sibling1 (myEvent)="...">è un evento vincolante per il genitore / host, perché è l'unico modo fornito da Angular. Il gestore di eventi chiama tuttavia un metodo sull'altro fratello ( com1). Il genitore è usato come mediatore.
Günter Zöchbauer,

Come chiamarlo fuori dalla vista ?! Proprio dentro somecomponent.ts?
Mohammad Kermani,

Ma se due diversi componenti (evento On Click ((un componente)) per un utente nell'elenco e copia quell'evento click nella pagina dei dettagli (un altro componente)) - Da un metodo Componente che voglio usare in un altro componente, come farlo Dimmelo, ti prego ??? @ GünterZöchbauer
Jignesh Vagh,

165

Innanzitutto, ciò di cui hai bisogno per capire le relazioni tra i componenti. Quindi puoi scegliere il giusto metodo di comunicazione. Proverò a spiegare tutti i metodi che conosco e utilizzo nella mia pratica per la comunicazione tra i componenti.

Quali tipi di relazioni tra i componenti possono esserci?

1. Genitore> Figlio

inserisci qui la descrizione dell'immagine

Condivisione dei dati tramite input

Questo è probabilmente il metodo più comune di condivisione dei dati. Funziona utilizzando il @Input()decoratore per consentire il passaggio dei dati tramite il modello.

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

}

Questo è un metodo molto semplice. È facile da usare. Possiamo anche rilevare le modifiche ai dati nel componente figlio utilizzando ngOnChanges .

Ma non dimenticare che se usiamo un oggetto come dati e cambiamo i parametri di questo oggetto, il riferimento ad esso non cambierà. Pertanto, se vogliamo ricevere un oggetto modificato in un componente figlio, deve essere immutabile.

2. Figlio> Genitore

inserisci qui la descrizione dell'immagine

Condivisione dei dati tramite ViewChild

ViewChild consente di iniettare un componente in un altro, dando al genitore l'accesso ai suoi attributi e funzioni. Un avvertimento, tuttavia, è che childnon sarà disponibile fino a dopo l'inizializzazione della vista. Ciò significa che è necessario implementare l'hook del ciclo di vita AfterViewInit per ricevere i dati dal figlio.

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

}

Condivisione dei dati tramite Output () ed EventEmitter

Un altro modo per condividere i dati è quello di emettere dati dal figlio, che possono essere elencati dal genitore. Questo approccio è ideale quando si desidera condividere le modifiche ai dati che si verificano su clic sui pulsanti, voci di modulo e altri eventi utente.

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

inserisci qui la descrizione dell'immagine

Figlio> Genitore> Figlio

Cerco di spiegare altri modi di comunicare tra fratelli di seguito. Ma potresti già capire uno dei modi di comprendere i metodi sopra.

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

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

bambini-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. Componenti non correlati

inserisci qui la descrizione dell'immagine

Tutti i metodi che ho descritto di seguito possono essere utilizzati per tutte le opzioni di cui sopra per la relazione tra i componenti. Ognuno ha i suoi vantaggi e svantaggi.

Condivisione dei dati con un servizio

Quando si trasmettono dati tra componenti privi di una connessione diretta, come fratelli, nipoti, ecc., È necessario utilizzare un servizio condiviso. Quando disponi di dati che dovrebbero essere sempre sincronizzati, trovo l'RxJS BehaviorSubject molto utile in questa situazione.

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

}

Condivisione dei dati con un percorso

A volte è necessario non solo passare dati semplici tra i componenti, ma salvare un po 'di stato della pagina. Ad esempio, vogliamo salvare alcuni filtri nel mercato online, quindi copiare questo link e inviarlo a un amico. E ci aspettiamo che apra la pagina nello stesso stato di noi. Il primo, e probabilmente il più veloce, modo per farlo sarebbe usare i parametri della query .

I parametri della query sono più simili a quelli di /people?id=doveid può eguagliare qualsiasi cosa e puoi avere tutti i parametri che desideri. I parametri della query sarebbero separati dal carattere e commerciale.

Quando si lavora con i parametri della query, non è necessario definirli nel file delle rotte e possono essere denominati parametri. Ad esempio, prendi il seguente codice:

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

}

Nella pagina di ricezione, riceveresti questi parametri di query come il seguente:

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

L'ultimo modo, che è più complicato ma più potente, è usare NgRx . Questa libreria non è per la condivisione dei dati; è una potente libreria di gestione dello stato. Non posso in un breve esempio spiegare come usarlo, ma puoi andare sul sito ufficiale e leggere la documentazione a riguardo.

Per me, NgRx Store risolve diversi problemi. Ad esempio, quando si ha a che fare con osservabili e quando la responsabilità di alcuni dati osservabili è condivisa tra diversi componenti, le azioni del negozio e il riduttore assicurano che le modifiche ai dati vengano sempre eseguite "nel modo giusto".

Fornisce inoltre una soluzione affidabile per la memorizzazione nella cache delle richieste HTTP. Sarai in grado di memorizzare le richieste e le loro risposte in modo da poter verificare che la richiesta che stai facendo non abbia ancora una risposta memorizzata.

Puoi leggere su NgRx e capire se ne hai bisogno nella tua app o meno:

Infine, voglio dire che prima di scegliere alcuni dei metodi per la condivisione dei dati è necessario capire come questi dati verranno utilizzati in futuro. Voglio dire, forse proprio ora puoi usare solo un @Inputdecoratore per condividere un nome utente e un cognome. Quindi aggiungerai un nuovo componente o un nuovo modulo (ad esempio un pannello di amministrazione) che richiede ulteriori informazioni sull'utente. Ciò significa che potrebbe essere un modo migliore per utilizzare un servizio per i dati dell'utente o un altro modo per condividere i dati. Devi pensarci di più prima di iniziare a implementare la condivisione dei dati.


3
migliore spiegazione con esempi
afeef

1
la migliore risposta a questo argomento che abbia mai visto, complimenti, ..
Lonely

Spiegazione eccellente
Moisés Aguilar

in 3. Fratelli germani il nome visualizzato di ts child-two.component.ts in html dovrebbe essere childMessage (nel caso utilizzare il file html)
Nam Nguyễn,

85

È possibile accedere al metodo del componente dal componente due ..

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

componente Due file html

<button (click)="callMe()">click</button>

2
Questo è stato per me fino a quando non ho avuto bisogno, ad esempio, di accedere a una variabile in componentOne da componentTwo invocando testCall .... il caso per cui il valore della variabile è impostato da componentOne ma componentTwo otterrà il valore predefinito e non quello corrente.
rey_coder,

2
Non ricevo valori componentOne variabili all'interno del metodo testCall se chiamo testCall da componentTwo. Qualche idea?
Raja,

Spiega questo metodo per Ionic 4 con Angular 7
Mohammad Ayoub Khan

in questo modo non trigerano @Raja che ha lo stesso problema
Kevin Dias,

1
THQQQQQQQQQQQ TANTO UOMO
Ashu

33

Componente 1 (figlio):

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

Buona risposta, vedi anche qui .
Cifra

va bene come chiamo function2 su html? ho sempre ricevuto this.component1 non è definito
cajuuh

<com1 # component1 (click) = "function2 ()"> </com1>
Ihor Khomiak

1
Questo ha funzionato per me, una volta capito che dovevi importare ViewChild da @ angular / core e anche Component1 da qualsiasi luogo.
Dallas Caley,

1
Invece di estendere la classe del componente, ho scoperto @ViewChildquale ha funzionato per me. Grazie per questo esempio
Yash,

27

Dipende dalla relazione tra i componenti (genitore / figlio) ma il modo migliore / generico per rendere i componenti di comunicazione consiste nell'utilizzare un servizio condiviso.

Vedi questo documento per maggiori dettagli:

Detto questo, è possibile utilizzare quanto segue per fornire un'istanza di com1 in com2:

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

In com2, puoi utilizzare quanto segue:

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

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}

sto ricevendo un errore Impossibile associare 'com1Ref' poiché non è una proprietà nativa nota
noobProgrammer

e non sta prendendo this.com1.function1 (); invece this.com1ref.function1 ();
noobProgrammer

Grazie per aver segnalato l'errore di battitura! Hai @Inputsul campo?
Thierry Templier,

ok funziona, puoi dirmi come ottenere lo stesso per la relazione genitore - figlio ??
noobProgrammer

1
ho provato lo stesso per il figlio genitore, ma dice function1 () di undegined
noobProgrammer

1
  • Diciamo che il 1 ° componente è DbstatsMainComponent
  • Componente DbstatsGraphComponent 2 ° componente.
  • 1 ° componente che chiama il metodo del 2 °

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

Nota la variabile locale #dbgraphsul componente figlio, che il genitore può usare per accedere ai suoi metodi ( dbgraph.displayTableGraph()).


0

Usando Dataservice possiamo chiamare la funzione da un altro componente

Component1: il componente che stiamo chiamando la funzione

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: il componente che contiene la funzione

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

 closeDrawer() {
        console.log("this is called")
    }

questo può chiamare più volte per evitare che usi questo codice all'interno del costruttoreif ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }
Shafeeq Mohammed,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.