Một lựa chọn khác.
OP đã hỏi một cách để sử dụng một cuộc gọi lại. Trong trường hợp này, anh ấy đã đề cập cụ thể đến một chức năng xử lý một sự kiện (trong ví dụ của anh ấy: một sự kiện nhấp chuột), sẽ được coi là câu trả lời được chấp nhận từ @serginho gợi ý: với @Output
vàEventEmitter
.
Tuy nhiên, có một sự khác biệt giữa một cuộc gọi lại và một sự kiện: Với một cuộc gọi lại, thành phần con của bạn có thể lấy một số phản hồi hoặc thông tin từ cha mẹ, nhưng một sự kiện chỉ có thể thông báo rằng có điều gì đó đã xảy ra mà không mong đợi bất kỳ phản hồi nào.
Có những trường hợp sử dụng trong đó một phản hồi là cần thiết, ví dụ. có được một màu, hoặc một danh sách các yếu tố mà thành phần cần xử lý. Bạn có thể sử dụng các hàm bị ràng buộc như một số câu trả lời đã đề xuất hoặc bạn có thể sử dụng các giao diện (đó luôn là sở thích của tôi).
Thí dụ
Giả sử bạn có một thành phần chung hoạt động trên danh sách các phần tử {id, name} mà bạn muốn sử dụng với tất cả các bảng cơ sở dữ liệu của bạn có các trường này. Thành phần này nên:
- lấy một loạt các yếu tố (trang) và hiển thị chúng trong một danh sách
- cho phép loại bỏ một yếu tố
- thông báo rằng một phần tử đã được nhấp, để cha mẹ có thể thực hiện một số hành động.
- cho phép lấy trang tiếp theo của các phần tử.
Thành phần con
Sử dụng liên kết thông thường, chúng ta sẽ cần 1 @Input()
và 3 @Output()
tham số (nhưng không có bất kỳ phản hồi nào từ cha mẹ). Ví dụ. <list-ctrl [items]="list" (itemClicked)="click($event)" (itemRemoved)="removeItem($event)" (loadNextPage)="load($event)" ...>
, nhưng tạo một giao diện, chúng ta sẽ chỉ cần một @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);
}
}
Thành phần phụ huynh
Bây giờ chúng ta có thể sử dụng thành phần danh sách trong cha mẹ.
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;
}
}
Lưu ý rằng <list-ctrl>
nhậnthis
(thành phần cha) là đối tượng gọi lại. Một lợi thế nữa là không bắt buộc phải gửi cá thể cha, nó có thể là một dịch vụ hoặc bất kỳ đối tượng nào thực hiện giao diện nếu trường hợp sử dụng của bạn cho phép.
Ví dụ đầy đủ là trên stackblitz này .
@Input
cách gợi ý đã làm cho mã spagetti của tôi và không dễ duy trì ..@Output
đó là một cách tự nhiên hơn nhiều để làm những gì tôi muốn. Kết quả là tôi đã thay đổi câu trả lời được chấp nhận