AngularJS有&参数,你可以把一个回调传递给一个指令(例如AngularJS的回调方式)。是否可以将回调函数作为@Input传递给Angular组件(如下所示)?如果不是,那最接近AngularJS的功能是什么?

@Component({
    selector: 'suggestion-menu',
    providers: [SuggestService],
    template: `
    <div (mousedown)="suggestionWasClicked(suggestion)">
    </div>`,
    changeDetection: ChangeDetectionStrategy.Default
})
export class SuggestionMenuComponent {
    @Input() callback: Function;

    suggestionWasClicked(clickedEntry: SomeModel): void {
        this.callback(clickedEntry, this.query);
    }
}


<suggestion-menu callback="insertSuggestion">
</suggestion-menu>

当前回答

我认为这是一个糟糕的解决方案。如果你想通过@Input()将Function传递给组件,@Output()装饰器就是你要找的。

export class SuggestionMenuComponent {
    @Output() onSuggest: EventEmitter<any> = new EventEmitter();

    suggestionWasClicked(clickedEntry: SomeModel): void {
        this.onSuggest.emit([clickedEntry, this.query]);
    }
}

<suggestion-menu (onSuggest)="insertSuggestion($event[0],$event[1])">
</suggestion-menu>

其他回答

我认为这是一个糟糕的解决方案。如果你想通过@Input()将Function传递给组件,@Output()装饰器就是你要找的。

export class SuggestionMenuComponent {
    @Output() onSuggest: EventEmitter<any> = new EventEmitter();

    suggestionWasClicked(clickedEntry: SomeModel): void {
        this.onSuggest.emit([clickedEntry, this.query]);
    }
}

<suggestion-menu (onSuggest)="insertSuggestion($event[0],$event[1])">
</suggestion-menu>

对于我来说,除了.bind(this)之外,我还必须在要执行的方法名称后面加上一对括号。

在父组件中:

在.ts文件中:

    this.pillTabs = [
        { tabName: 'Subscribers', tabMethod: this.showSubscribers.bind(this) },
        { tabName: 'Exemplars', tabMethod: this.showExemplars.bind(this) }
    ];

在.html文件中:

    <pill-tabs [pillTabs]="pillTabs"></pill-tabs>

在Child组件中:

在.ts文件中:

    @Input() pillTabs: PillTab[];

在.html文件中:

    <div *ngFor="let pillTab of pillTabs; let i = index">
        <input type="radio" id="{{'radio-' + i}}" name="tabs" [checked]="pillTab.checked"
            (click)="pillTab.tabMethod()" />
        <label class="tab" for="{{'radio-' + i}}">{{pillTab.tabName}}</label>
    </div>

当我没有对括号后面的方法时,代码是不工作的:

      (click)="pillTab.tabMethod"

然后当我在这里加上一对括号,代码就开始工作了。

      (click)="pillTab.tabMethod()"

我希望有人觉得它有帮助。

目前的答案可以简化为……

@Component({
  ...
  template: '<child [myCallback]="theCallback"></child>',
  directives: [ChildComponent]
})
export class ParentComponent{
  public theCallback(){
    ...
  }
}

@Component({...})
export class ChildComponent{
  //This will be bound to the ParentComponent.theCallback
  @Input()
  public myCallback: Function; 
  ...
}

另一个选择。

OP询问了一种使用回调的方法。在这种情况下,他特别提到了一个处理事件的函数(在他的例子中:一个点击事件),这应该被视为@serginho所建议的接受答案:使用@Output和EventEmitter。

然而,回调和事件之间是有区别的:使用回调,您的子组件可以从父组件检索一些反馈或信息,但事件只能通知发生的事情,而不期望任何反馈。

有一些用例需要反馈,例如获取一个颜色,或者组件需要处理的元素列表。您可以像一些回答所建议的那样使用绑定函数,也可以使用接口(这一直是我的偏好)。

例子

让我们假设您有一个通用组件,它对元素{id, name}列表进行操作,您希望将其用于具有这些字段的所有数据库表。这个组件应该:

检索一系列元素(页面)并在列表中显示它们 允许移除一个元素 通知元素被单击,以便父元素可以采取一些操作。 允许检索元素的下一页。

子组件

使用普通的绑定,我们需要1个@Input()和3个@Output()参数(但是没有来自父类的任何反馈)。例:<list-ctrl [items]="list" (itemClicked)="click($event)"(itemRemoved) = " removeItem(事件)”(loadNextPage) =“负载(事件)美元”...>,但是创建一个接口我们只需要一个@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); 
    }
}

父组件

现在我们可以在父类中使用list组件。

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上。

与Max Fahl给出的答案不同。

你可以将回调函数定义为父组件中的箭头函数,这样你就不需要绑定它了。

@Component({
  ...
  // unlike this, template: '<child [myCallback]="theCallback.bind(this)"></child>',
  template: '<child [myCallback]="theCallback"></child>',
  directives: [ChildComponent]
})
export class ParentComponent {

   // unlike this, public theCallback(){
   public theCallback = () => {
    ...
  }
}

@Component({...})
export class ChildComponent{
  //This will be bound to the ParentComponent.theCallback
  @Input()
  public myCallback: Function; 
  ...
}