Une autre alternative.
L'OP a demandé un moyen d'utiliser un rappel. Dans ce cas, il faisait spécifiquement référence à une fonction qui traite un événement (dans son exemple: un événement de clic), qui doit être traitée comme la réponse acceptée de @serginho le suggère: avec @Output
et EventEmitter
.
Cependant, il existe une différence entre un rappel et un événement: avec un rappel, votre composant enfant peut récupérer des commentaires ou des informations du parent, mais un événement peut uniquement informer que quelque chose s'est produit sans attendre de retour.
Il existe des cas d'utilisation où une rétroaction est nécessaire, par ex. obtenir une couleur ou une liste d'éléments que le composant doit gérer. Vous pouvez utiliser des fonctions liées comme certaines réponses l'ont suggéré, ou vous pouvez utiliser des interfaces (c'est toujours ma préférence).
Exemple
Supposons que vous ayez un composant générique qui opère sur une liste d'éléments {id, nom} que vous souhaitez utiliser avec toutes vos tables de base de données qui contiennent ces champs. Cette composante devrait:
- récupérer une gamme d'éléments (page) et les afficher dans une liste
- permettre de supprimer un élément
- informer qu'un élément a été cliqué, afin que le parent puisse prendre des mesures.
- permet de récupérer la page suivante des éléments.
Composant enfant
En utilisant une liaison normale, nous aurions besoin de 1 @Input()
et 3 @Output()
paramètres (mais sans aucun retour du parent). Ex. <list-ctrl [items]="list" (itemClicked)="click($event)" (itemRemoved)="removeItem($event)" (loadNextPage)="load($event)" ...>
, mais pour créer une interface, nous n'en aurons besoin que d'une seule @Input()
:
import {Component, Input, OnInit} from '@angular/core';
export interface IdName{
id: number;
name: string;
}
export interface IListComponentCallback<T extends IdName> {
getList(page: number, limit: number): Promise< T[] >;
removeItem(item: T): Promise<boolean>;
click(item: T): void;
}
@Component({
selector: 'list-ctrl',
template: `
<button class="item" (click)="loadMore()">Load page {{page+1}}</button>
<div class="item" *ngFor="let item of list">
<button (click)="onDel(item)">DEL</button>
<div (click)="onClick(item)">
Id: {{item.id}}, Name: "{{item.name}}"
</div>
</div>
`,
styles: [`
.item{ margin: -1px .25rem 0; border: 1px solid #888; padding: .5rem; width: 100%; cursor:pointer; }
.item > button{ float: right; }
button.item{margin:.25rem;}
`]
})
export class ListComponent implements OnInit {
@Input() callback: IListComponentCallback<IdName>; // <-- CALLBACK
list: IdName[];
page = -1;
limit = 10;
async ngOnInit() {
this.loadMore();
}
onClick(item: IdName) {
this.callback.click(item);
}
async onDel(item: IdName){
if(await this.callback.removeItem(item)) {
const i = this.list.findIndex(i=>i.id == item.id);
this.list.splice(i, 1);
}
}
async loadMore(){
this.page++;
this.list = await this.callback.getList(this.page, this.limit);
}
}
Composant parent
Nous pouvons maintenant utiliser le composant liste dans le parent.
import { Component } from "@angular/core";
import { SuggestionService } from "./suggestion.service";
import { IdName, IListComponentCallback } from "./list.component";
type Suggestion = IdName;
@Component({
selector: "my-app",
template: `
<list-ctrl class="left" [callback]="this"></list-ctrl>
<div class="right" *ngIf="msg">{{ msg }}<br/><pre>{{item|json}}</pre></div>
`,
styles:[`
.left{ width: 50%; }
.left,.right{ color: blue; display: inline-block; vertical-align: top}
.right{max-width:50%;overflow-x:scroll;padding-left:1rem}
`]
})
export class ParentComponent implements IListComponentCallback<Suggestion> {
msg: string;
item: Suggestion;
constructor(private suggApi: SuggestionService) {}
getList(page: number, limit: number): Promise<Suggestion[]> {
return this.suggApi.getSuggestions(page, limit);
}
removeItem(item: Suggestion): Promise<boolean> {
return this.suggApi.removeSuggestion(item.id)
.then(() => {
this.showMessage('removed', item);
return true;
})
.catch(() => false);
}
click(item: Suggestion): void {
this.showMessage('clicked', item);
}
private showMessage(msg: string, item: Suggestion) {
this.item = item;
this.msg = 'last ' + msg;
}
}
Notez que le <list-ctrl>
reçoit this
(composant parent) en tant qu'objet de rappel. Un avantage supplémentaire est qu'il n'est pas nécessaire d'envoyer l'instance parente, il peut s'agir d'un service ou de tout objet qui implémente l'interface si votre cas d'utilisation le permet.
L'exemple complet se trouve sur ce stackblitz .
@Input
manière suggérée a rendu mon code spagetti et pas facile à entretenir ..@Output
s sont une façon beaucoup plus naturelle de faire ce que je veux. En conséquence, j'ai changé la réponse acceptée