Comment appeler une autre fonction de composants dans angular2

156

J'ai deux composants comme suit et je souhaite appeler une fonction à partir d'un autre composant. Les deux composants sont inclus dans le troisième composant parent using directive.

Composant 1:

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

Composante 2:

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

J'ai essayé d'utiliser @inputet @outputmais je ne comprends pas exactement comment l'utiliser et comment appeler cette fonction, quelqu'un peut-il m'aider?

noobProgrammer
la source

Réponses:

130

Si com1 et com2 sont frères, vous pouvez utiliser

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

com2 émet un événement en utilisant un EventEmitter

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

Ici, le composant parent ajoute une liaison d'événement pour écouter les myEventévénements, puis appelle com1.function1()lorsqu'un tel événement se produit. #com1est une variable de modèle qui permet de faire référence à cet élément ailleurs dans le modèle. Nous l' utilisons pour faire function1()le gestionnaire d'événements pour myEventde com2:

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

Pour d'autres options de communication entre les composants, voir également interaction avec les composants

Günter Zöchbauer
la source
Votre question ne contient rien sur le parent-enfant. Qu'essayez-vous d'accomplir?
Günter Zöchbauer
quand vous avez commencé à dire "Ici, le composant parent ajoute une liaison d'événement pour écouter myEvent", je suis très confus. Je pensais que nous essayions de résoudre la situation des composants frères et sœurs. Le lien angulaire est entièrement parent-enfant, je ne peux donc pas les utiliser.
Angela P le
C'est le parent des frères et sœurs (vous pouvez aussi dire hôte). <sibling1 (myEvent)="...">est une liaison d'événement pour le parent / hôte, car c'est le seul moyen fourni par Angular. Le gestionnaire d'événements appelle cependant une méthode sur l'autre frère ( com1). Le parent est utilisé comme médiateur.
Günter Zöchbauer
Comment l'appeler hors de la vue?! Juste à l'intérieur du somecomponent.ts?
Mohammad Kermani
Mais si deux composants différents (événement On Click ((un composant)) pour un utilisateur dans la liste et copiez cet événement click sur la page de détail (un autre composant)) - À partir de la méthode One Component que je souhaite utiliser dans un autre composant, comment faire S'il vous plaît dites-moi ??? @ GünterZöchbauer
Jignesh Vagh
165

Tout d'abord, ce dont vous avez besoin pour comprendre les relations entre les composants. Ensuite, vous pouvez choisir la bonne méthode de communication. Je vais essayer d'expliquer toutes les méthodes que je connais et utilise dans ma pratique pour la communication entre les composants.

Quels types de relations entre les composants peut-il y avoir?

1. Parent> Enfant

entrez la description de l'image ici

Partage de données via l'entrée

C'est probablement la méthode la plus courante de partage de données. Cela fonctionne en utilisant le @Input()décorateur pour permettre aux données d'être transmises via le modèle.

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

}

C'est une méthode très simple. C'est facile a utiliser. Nous pouvons également détecter les modifications apportées aux données dans le composant enfant en utilisant ngOnChanges .

Mais n'oubliez pas que si nous utilisons un objet comme données et modifions les paramètres de cet objet, la référence à celui-ci ne changera pas. Par conséquent, si nous voulons recevoir un objet modifié dans un composant enfant, il doit être immuable.

2. Enfant> Parent

entrez la description de l'image ici

Partage de données via ViewChild

ViewChild permet à un composant d'être injecté dans un autre, donnant au parent l'accès à ses attributs et fonctions. Une mise en garde, cependant, est qu'il childne sera disponible qu'après l'initialisation de la vue. Cela signifie que nous devons implémenter le hook de cycle de vie AfterViewInit pour recevoir les données de l'enfant.

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

}

Partage de données via Output () et EventEmitter

Une autre façon de partager des données consiste à émettre des données de l'enfant, qui peuvent être répertoriées par le parent. Cette approche est idéale lorsque vous souhaitez partager des modifications de données qui se produisent sur des éléments tels que des clics sur des boutons, des entrées de formulaire et d'autres événements utilisateur.

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. Frères et sœurs

entrez la description de l'image ici

Enfant> Parent> Enfant

J'essaie d'expliquer ci-dessous d'autres moyens de communiquer entre frères et sœurs. Mais vous pouvez déjà comprendre l'une des façons de comprendre les méthodes ci-dessus.

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. Composants indépendants

entrez la description de l'image ici

Toutes les méthodes que j'ai décrites ci-dessous peuvent être utilisées pour toutes les options ci-dessus pour la relation entre les composants. Mais chacun a ses propres avantages et inconvénients.

Partage de données avec un service

Lorsque vous passez des données entre des composants qui n'ont pas de connexion directe, tels que des frères et sœurs, des petits-enfants, etc., vous devez utiliser un service partagé. Lorsque vous avez des données qui doivent toujours être synchronisées, je trouve le RxJS BehaviorSubject très utile dans cette 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")
  }

}

Partage de données avec un itinéraire

Parfois, vous devez non seulement transmettre des données simples entre les composants, mais également enregistrer un état de la page. Par exemple, nous voulons enregistrer un filtre sur le marché en ligne, puis copier ce lien et l'envoyer à un ami. Et nous nous attendons à ce qu'il ouvre la page dans le même état que nous. La première, et probablement la plus rapide, pour ce faire serait d'utiliser des paramètres de requête .

Les paramètres de requête ressemblent davantage à /people?id=idpeut égaler tout et vous pouvez avoir autant de paramètres que vous le souhaitez. Les paramètres de requête seraient séparés par le caractère esperluette.

Lorsque vous travaillez avec des paramètres de requête, vous n'avez pas besoin de les définir dans votre fichier de routes, et ils peuvent être nommés paramètres. Par exemple, prenez le code suivant:

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

}

Dans la page de réception, vous recevrez ces paramètres de requête comme suit:

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

Le dernier moyen, plus compliqué mais plus puissant, est d'utiliser NgRx . Cette bibliothèque n'est pas destinée au partage de données; c'est une puissante bibliothèque de gestion d'état. Je ne peux pas dans un court exemple expliquer comment l'utiliser, mais vous pouvez aller sur le site officiel et lire la documentation à ce sujet.

Pour moi, NgRx Store résout plusieurs problèmes. Par exemple, lorsque vous avez affaire à des observables et lorsque la responsabilité de certaines données observables est partagée entre différents composants, les actions de stockage et le réducteur garantissent que les modifications de données seront toujours effectuées «de la bonne manière».

Il fournit également une solution fiable pour la mise en cache des requêtes HTTP. Vous pourrez stocker les demandes et leurs réponses afin de pouvoir vérifier que la demande que vous faites n'a pas encore de réponse stockée.

Vous pouvez en savoir plus sur NgRx et comprendre si vous en avez besoin dans votre application ou non:

Enfin, je tiens à dire qu'avant de choisir certaines des méthodes de partage de données, vous devez comprendre comment ces données seront utilisées à l'avenir. Je veux dire peut-être que maintenant, vous pouvez utiliser juste un @Inputdécorateur pour partager un nom d'utilisateur et un nom de famille. Ensuite, vous ajouterez un nouveau composant ou un nouveau module (par exemple, un panneau d'administration) qui a besoin de plus d'informations sur l'utilisateur. Cela signifie que cela peut être une meilleure façon d'utiliser un service pour les données utilisateur ou une autre façon de partager des données. Vous devez y réfléchir davantage avant de commencer à mettre en œuvre le partage de données.

Roman Skydan
la source
3
meilleure explication avec des exemples
afeef
1
la meilleure réponse à ce sujet que j'ai jamais vue, félicitations, ..
Lonely
Excellente explication
Moisés Aguilar
dans 3. Frères et sœurs, le nom d'affichage ts child-two.component.ts en html doit être childMessage (pour le cas, utilisez un fichier html)
Nam Nguyễn
85

Vous pouvez accéder à la méthode du composant un à partir du composant deux.

componentOne

  ngOnInit() {}

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

composantDeux

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


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


constructor(private comp: oneComponent ) { }

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

componentTwo fichier html

<button (click)="callMe()">click</button>
Jayantha
la source
2
C'était tout pour moi jusqu'à ce que je devais par exemple accéder à une variable dans componentOne à partir de componentTwo en invoquant testCall .... le cas pour lequel la valeur de la variable est définie par componentOne mais componentTwo obtiendra la valeur par défaut et non l'actuelle.
rey_coder
2
Je n'obtiens pas de valeurs de variable componentOne dans la méthode testCall si j'appelle testCall à partir de componentTwo. Une idée?
Raja
Veuillez expliquer cette méthode pour Ionic 4 avec Angular 7
Mohammad Ayoub Khan
avec de cette façon, il ne triger pas @Raja ayant le même problème
Kevin Dias
1
THQQQQQQQQQQ SO MUCH MAN
Ashu
33

Composant 1 (enfant):

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

Composant 2 (parent):

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

  function2(){
    this.component1.function1();
  }
}
Ihor Khomiak
la source
Bonne réponse, voir aussi ici .
Chiffre le
ok comment j'appelle function2 sur html? i always got this.component1 is undefined
cajuuh
<com1 # component1 (click) = "function2 ()"> </com1>
Ihor Khomiak
1
Cela a fonctionné pour moi, une fois que j'ai réalisé que vous deviez importer ViewChild à partir de @ angular / core et également Component1 où qu'il se trouve.
Dallas Caley
1
Au lieu d'étendre la classe de composants, j'ai uesd @ViewChildqui a fonctionné pour moi. Merci pour cet exemple.
Yash
27

Cela dépend de la relation entre vos composants (parent / enfant) mais la meilleure façon / générique de faire communiquer des composants est d'utiliser un service partagé.

Voir ce document pour plus de détails:

Cela étant dit, vous pouvez utiliser ce qui suit pour fournir une instance de com1 dans com2:

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

Dans com2, vous pouvez utiliser les éléments suivants:

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

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}
Thierry Templier
la source
je reçois une erreur Impossible de se lier à 'com1Ref' car ce n'est pas une propriété native connue
noobProgrammer
et il ne prend pas this.com1.function1 (); à la place this.com1ref.function1 ();
noobProgrammer
Merci d'avoir signalé la faute de frappe! Avez-vous @Inputle terrain?
Thierry Templier le
ok ça marche, pouvez-vous me dire comment y parvenir pour la relation parent-enfant
noobProgrammer
1
J'ai essayé la même chose pour l'enfant parent mais il dit function1 () de non défini
noobProgrammer
1
  • Disons que le premier composant est DbstatsMainComponent
  • 2ème composant DbstatsGraphComponent.
  • 1er composant appelant la méthode du 2ème

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

Notez la variable locale #dbgraphsur le composant enfant, que le parent peut utiliser pour accéder à ses méthodes ( dbgraph.displayTableGraph()).

RAHUL KUMAR
la source
0

En utilisant Dataservice, nous pouvons appeler la fonction à partir d'un autre composant

Component1: Le composant que nous appelons la fonction

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: le composant qui contient la fonction

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

 closeDrawer() {
        console.log("this is called")
    }
Shafeeq Mohammed
la source
cela peut appeler plusieurs fois pour éviter d'utiliser ce code dans le constructeurif ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }
Shafeeq Mohammed