다른 대안.
OP는 콜백을 사용하는 방법을 물었습니다. 이 경우 그는 이벤트 (예 : 클릭 이벤트)를 처리하는 함수를 구체적으로 언급 한 것으로 @serginho의 제안 된 답변 : with @Output
및 으로 처리됩니다 EventEmitter
.
그러나 콜백과 이벤트에는 차이가 있습니다. 콜백을 사용하면 자식 구성 요소가 부모로부터 피드백이나 정보를 검색 할 수 있지만 이벤트는 피드백을 예상하지 않고 어떤 일이 발생했음을 알릴 수 있습니다.
피드백이 필요한 사용 사례가 있습니다 (예 : 구성 요소가 처리해야하는 색상 또는 요소 목록을 가져옵니다. 일부 답변에서 제안한대로 바운드 함수를 사용하거나 인터페이스를 사용할 수 있습니다 (항상 선호합니다).
예
이러한 필드가있는 모든 데이터베이스 테이블에 사용하려는 {id, name} 요소 목록에서 작동하는 일반 구성 요소가 있다고 가정하십시오. 이 구성 요소는 다음을 수행해야합니다.
- 다양한 요소 (페이지)를 검색하여 목록에 표시
- 요소를 제거 할 수있다
- 요소를 클릭했음을 알리면 부모가 조치를 취할 수 있습니다.
- 요소의 다음 페이지를 검색 할 수 있습니다.
자식 구성 요소
일반 바인딩을 사용하면 1 @Input()
및 3 @Output()
매개 변수 가 필요 하지만 부모의 피드백은 없습니다. 전의. <list-ctrl [items]="list" (itemClicked)="click($event)" (itemRemoved)="removeItem($event)" (loadNextPage)="load($event)" ...>
인터페이스를 만들려면 하나만 필요합니다 @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);
}
}
부모 구성 요소
이제 부모에서 목록 구성 요소를 사용할 수 있습니다.
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;
}
}
(가) 참고 <list-ctrl>
수신 this
콜백 목적으로 (상위 성분). 추가 이점 중 하나는 상위 인스턴스를 전송할 필요가 없다는 것입니다. 사용 사례에서 허용하는 경우 인터페이스를 구현하는 서비스 또는 객체 일 수 있습니다.
완전한 예제는 이 stackblitz에 있습니다.
@Input
내 코드를 spagetti로 만들고 유지 관리하기 쉽지 않은 방법을 제안했습니다@Output
. 그 결과 내가 허용 대답을 변경