Kolejna alternatywa.
OP poprosił o sposób oddzwonienia. W tym przypadku odnosił się konkretnie do funkcji przetwarzającej zdarzenie (w jego przykładzie: zdarzenie kliknięcia), która powinna być traktowana jako zaakceptowana odpowiedź z @serginho: za pomocą @Output
iEventEmitter
.
Istnieje jednak różnica między wywołaniem zwrotnym a zdarzeniem: dzięki wywołaniu zwrotnemu komponent podrzędny może uzyskać informacje zwrotne lub informacje od rodzica, ale zdarzenie może tylko poinformować, że coś się wydarzyło bez oczekiwania na informację zwrotną.
Istnieją przypadki użycia, w których konieczne jest sprzężenie zwrotne, np. uzyskać kolor lub listę elementów, które komponent musi obsłużyć. Możesz użyć funkcji powiązanych, jak sugerują niektóre odpowiedzi, lub możesz użyć interfejsów (to zawsze moja preferencja).
Przykład
Załóżmy, że masz ogólny komponent, który działa na liście elementów {id, name}, których chcesz używać ze wszystkimi tabelami bazy danych zawierającymi te pola. Ten komponent powinien:
- pobierz zakres elementów (stronę) i pokaż je na liście
- zezwól na usunięcie elementu
- informuje, że element został kliknięty, aby rodzic mógł podjąć pewne działania.
- zezwól na pobranie następnej strony elementów.
Komponent potomny
Używając normalnego wiązania potrzebowalibyśmy 1 @Input()
i 3 @Output()
parametry (ale bez żadnej informacji zwrotnej od rodzica). Dawny. <list-ctrl [items]="list" (itemClicked)="click($event)" (itemRemoved)="removeItem($event)" (loadNextPage)="load($event)" ...>
, ale do utworzenia interfejsu potrzebujemy tylko jednego @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);
}
}
Komponent macierzysty
Teraz możemy użyć komponentu listy w elemencie nadrzędnym.
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;
}
}
Zauważ, że <list-ctrl>
odbierathis
(komponent nadrzędny) jako obiekt wywołania zwrotnego. Dodatkową zaletą jest to, że nie jest wymagane wysyłanie instancji nadrzędnej, może to być usługa lub dowolny obiekt implementujący interfejs, jeśli pozwala na to przypadek użycia.
Kompletny przykład jest na tym stackblitz .
@Input
sposób zasugerował, że mój kod spagetti i nie jest łatwy w utrzymaniu ..@Output
s są znacznie bardziej naturalnym sposobem robienia tego, co chcę. W rezultacie zmieniłem przyjętą odpowiedź