J'ai deux composants comme suit et je veux appeler une fonction d'un autre composant. Les deux composants sont inclus dans le troisième composant parent via 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 @input
et @output
mais je ne comprends pas exactement comment l'utiliser et comment appeler cette fonction. Quelqu'un peut-il aider?
Si com1 et com2 sont frères et soeurs, vous pouvez utiliser
@component({
selector:'com1',
})
export class com1{
function1(){...}
}
com2 émet un événement à l'aide d'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 événements myEvent
, puis appelle com1.function1()
lorsqu'un tel événement se produit. #com1
est une variable de modèle qui permet de faire référence à cet élément depuis un autre endroit du modèle. Nous l'utilisons pour faire de function1()
le gestionnaire d'événements pour myEvent
de com2
:
@component({
selector:'parent',
template: `<com1 #com1></com1><com2 (myEvent)="com1.function1()"></com2>`
)
export class com2{
}
Pour d'autres options de communication entre composants, voir aussi composant-interaction
Vous pouvez accéder à la méthode de composant one à partir du composant deux.
composantOne
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();
}
composantDeux fichiers html
<button (click)="callMe()">click</button>
Tout d’abord, vous devez comprendre les relations entre les composants. Ensuite, vous pouvez choisir le bon mode de communication. Je vais essayer d’expliquer toutes les méthodes que je connais et que j’utilise dans ma pratique pour la communication entre composants.
Quels types de relations entre composants peuvent-il exister?
1. Parent> Enfant
Partage de données via une entrée
C'est probablement la méthode la plus courante de partage de données. Cela fonctionne en utilisant le décorateur @Input()
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 à l'aide de ngOnChanges .
Mais n'oubliez pas que si nous utilisons un objet en tant que données et en modifions les paramètres, sa référence ne changera pas. Par conséquent, si nous voulons recevoir un objet modifié dans un composant enfant, il doit être immuable.
2. Enfant> Parent
Partage de données via ViewChild
ViewChild permet à un composant d'être injecté dans un autre, donnant ainsi au parent un accès à ses attributs et fonctions. Un inconvénient, cependant, est que child
ne sera disponible qu’après l’initialisation de la vue. Cela signifie que nous devons implémenter le hook AfterViewInit Lifecycle 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 les clics sur les boutons, les 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
Enfant> Parent> Enfant
J'essaie d'expliquer d'autres moyens de communiquer entre frères et soeurs ci-dessous. Mais vous pouviez déjà comprendre l’un des moyens 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 non liés
Toutes les méthodes décrites ci-dessous peuvent être utilisées pour toutes les options ci-dessus pour la relation entre les composants. Mais chacun a ses avantages et ses inconvénients.
Partage de données avec un service
Lorsque vous transmettez des données entre des composants dépourvus de connexion directe, tels que des frères et soeurs, des petits-enfants, etc., vous devez disposer d'un service partagé. Lorsque vous avez des données qui devraient toujours être synchronisées, je trouve le BehaviorSubject de RxJS 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 une route
Parfois, vous devez non seulement transmettre des données simples entre composants, mais également enregistrer un certain état de la page. Par exemple, nous souhaitons enregistrer certains filtres sur le marché en ligne, puis copier ce lien et les envoyer à un ami. Et nous nous attendons à ce qu'il ouvre la page dans le même état que nous. Le premier moyen, et probablement le plus rapide, de le faire serait d'utiliser paramètres de requête .
Les paramètres de requête ressemblent davantage à ceux de /people?id=
où id
peut être égal à n'importe quoi et vous pouvez avoir autant de paramètres que vous le souhaitez. Les paramètres de la 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 d’itinéraires. Vous pouvez les nommer paramètres. Par exemple, prenons 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 recevriez 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, consiste à utiliser NgRx . Cette bibliothèque n'est pas pour le partage de données; c'est une puissante bibliothèque de gestion d'état. Dans un court exemple, je ne peux pas 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 devez gérer des éléments observables et lorsque la responsabilité de certaines données observables est partagée entre différents composants, les actions de la banque d'informations et le réducteur garantissent que les modifications des données seront toujours effectuées "de la bonne manière".
Il fournit également une solution fiable pour la mise en cache des demandes HTTP. Vous pourrez stocker les demandes et leurs réponses afin de 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 savoir si vous en avez besoin ou non dans votre application:
Enfin, je tiens à dire qu'avant de choisir certaines méthodes de partage de données, vous devez comprendre comment ces données seront utilisées à l'avenir. Je veux dire que peut-être que vous pouvez utiliser juste un décorateur @Input
pour partager un nom d'utilisateur et un nom de famille. Ensuite, vous ajouterez un nouveau composant ou un nouveau module (un panneau d’administration, par exemple) qui nécessite davantage d’informations sur l’utilisateur. Cela signifie que cela peut être un meilleur moyen d'utiliser un service pour les données utilisateur ou un autre moyen de partager des données. Vous devez y penser davantage avant de commencer à mettre en œuvre le partage de données.
Cela dépend de la relation entre vos composants (parent/enfant), mais le meilleur moyen générique de faire communiquer les composants est d'utiliser un service partagé.
Voir cette doc pour plus de détails:
Ceci étant dit, vous pouvez utiliser ce qui suit pour fournir une instance du 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();
}
}
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();
}
}
<button (click)="dbgraph.displayTableGraph()">Graph</button> <dbstats-graph #dbgraph></dbstats-graph>
Notez la variable locale #dbgraph
sur le composant enfant, que le parent peut utiliser pour accéder à ses méthodes (dbgraph.displayTableGraph()
).
En utilisant Dataservice, nous pouvons appeler la fonction depuis un autre composant
Composant1: Le composant dont 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();
}
Composant2: Le composant qui contient la fonction
constructor( public bookmarkRoot: dataService ) {
this.bookmarkRoot.callToggle.subscribe(( data ) => {
this.closeDrawer();
} )
}
closeDrawer() {
console.log("this is called")
}