作为@Input的子组件的角度传递回调函数

Mic*_*dis 183 angularjs typescript angular

AngularJS有&参数,你可以将回调传递给指令(例如AngularJS的回调方式.是否可以将回调作为@InputAngular组件传递(如下所示)?如果不是,最接近什么是什么? 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>
Run Code Online (Sandbox Code Playgroud)

Ser*_*nho 256

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

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>
Run Code Online (Sandbox Code Playgroud)

  • 确切地说,您没有传递函数,而是将侦听器事件侦听器连接到输出.有助于理解它的工作原理. (36认同)
  • 这是一个很好的方法,但在阅读完这个答案后,我留下了很多问题.我希望它会更深入,或者提供描述`@ Output`和`EventEmitter`的链接.那么,这里是@******的Angular文档(https://angular.io/docs/ts/latest/cookbook/component-communication.html#parent-listens-for-child-event)有兴趣的人. (12认同)
  • 对于80%的情况可能是好的,但是当子组件想要以回调是否存在为条件进行可视化时则不行. (6认同)
  • 这对于单向绑定很好.你可以联系孩子的事件.但是你不能将回调函数传递给子进程并让它分析回调的返回值.下面的答案允许这样做. (5认同)
  • 我希望对为什么偏爱一种方式而不是“我认为这是一个糟糕的解决方案”有更多的解释。 (2认同)
  • @MichailMichailidis 关于您的链接。(并忽略角度模板中的“this”指针)我认为如果您需要 ChildComponent 中的动态函数,这很有用。例如,如果您有一个按钮在执行任何操作之前会打开另一个确认按钮,并且您需要在两个按钮之间添加一个钩子,例如 beforeConfirm。您想将按钮和确认按钮封装在同一个组件中,在这种情况下,使用`@Input` 传递回调是正确的解决方案。但是做 99% 组件的正确方法是使用 `@Output`。 (2认同)
  • 如果孩子想将其传递给孙子组件等,会发生什么?更多@Outputs?我们是否必须在每一层上定义一个函数才能作为输出传递到下一层?我认为对于那些传递实际回调的情况更好。或者存在其他解决方案?顺便说一句,我个人认为通过绑定传递参数根本不是繁琐的代码。我认为这是标准的js实践。我是唯一一个? (2认同)

Sna*_*ops 102

UPDATE

当Angular 2仍处于alpha状态并且许多功能不可用/未记录时,提交了此答案.虽然以下仍然可行,但这种方法现在已经过时了.我强烈建议接受以下答复.

原始答案

是的,实际上是,但是你需要确保它的范围是正确的.为此,我使用了一个属性来确保这this意味着我想要它.

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

  public ngOnInit(){
    this.theBoundCallback = this.theCallback.bind(this);
  }

  public theCallback(){
    ...
  }
}

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

  • @Output和EventEmitter适用于单向绑定.您可以连接到子事件,但是您不能将回调函数传递给子项,并让它分析回调的返回值.这个答案允许. (7认同)
  • 很棒的答案!我通常不会在绑定时重命名该函数.在`ngOnInit`我只会使用:`this.theCallback = this.theCallback.bind(this)`然后你可以传递`theCallback`而不是`theBoundCallback`. (3认同)
  • 代替`public theCallback(){..}`,你可以声明一个箭头函数`theCallback = () =&gt; {...};`并删除这条指令`this.theBoundCallback = this.theCallback.bind(this); ` (2认同)

Max*_*ahl 26

SnareChops给出的答案的替代方案.

您可以在模板中使用.bind(this)来产生相同的效果.它可能不是那么干净但它可以节省几行.我目前正处于角度2.4.0

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

  public theCallback(){
    ...
  }
}

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

  • 正如其他人所评论的那样,模板中的bind(this)无法记录,因此将来可能会被弃用/不支持.再加上`@Input`导致代码成为意大利面并使用`@ Output`导致更自然/未解决的过程 (2认同)
  • 当您将 bind() 放在模板中时,Angular 会在每次更改检测时重新评估此表达式。另一种解决方案 - 在模板之外进行绑定 - 不太简洁,但它没有这个问题。 (2认同)

Dan*_*sky 11

在某些情况下,您可能需要由父组件执行业务逻辑。在下面的示例中,我们有一个子组件,该组件根据父组件提供的逻辑来渲染表行:

@Component({
  ...
  template: '<table-component [getRowColor]="getColor"></table-component>',
  directives: [TableComponent]
})
export class ParentComponent {

 // Pay attention on the way this function is declared. Using fat arrow (=>) declaration 
 // we can 'fixate' the context of `getColor` function
 // so that it is bound to ParentComponent as if .bind(this) was used.
 getColor = (row: Row) => {
    return this.fancyColorService.getUserFavoriteColor(row);
 }

}

@Component({...})
export class TableComponent{
  // This will be bound to the ParentComponent.getColor. 
  // I found this way of declaration a bit safer and convenient than just raw Function declaration
  @Input('getRowColor') getRowColor: (row: Row) => Color;

  renderRow(){
    ....
    // Notice that `getRowColor` function holds parent's context because of a fat arrow function used in the parent
    const color = this.getRowColor(row);
    renderRow(row, color);
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,我想在这里演示两件事:

  1. 粗箭头(=>)代替.bind(this)函数来保存正确的上下文;
  2. 子组件中回调函数的Typesafe声明。

  • 使用提示:确保输入 `[getRowColor]="getColor"` 而不是 `[getRowColor]="getColor()"` ;-) (9认同)
  • 关于使用粗箭头代替“.bind(this)”的很好的解释 (3认同)

jea*_*ara 11

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; 
  ...
}
Run Code Online (Sandbox Code Playgroud)


小智 5

例如,我使用的是登录模式窗口,其中模式窗口是父窗口,登录表单是子窗口,而登录按钮则回调到模式父窗口的关闭功能。

父模态包含关闭模态的功能。此父级将close函数传递给登录子级组件。

import { Component} from '@angular/core';
import { LoginFormComponent } from './login-form.component'

@Component({
  selector: 'my-modal',
  template: `<modal #modal>
      <login-form (onClose)="onClose($event)" ></login-form>
    </modal>`
})
export class ParentModalComponent {
  modal: {...};

  onClose() {
    this.modal.close();
  }
}
Run Code Online (Sandbox Code Playgroud)

子级登录组件提交登录表单后,它将使用父级的回调函数关闭父级模式

import { Component, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'login-form',
  template: `<form (ngSubmit)="onSubmit()" #loginForm="ngForm">
      <button type="submit">Submit</button>
    </form>`
})
export class ChildLoginComponent {
  @Output() onClose = new EventEmitter();
  submitted = false;

  onSubmit() {
    this.onClose.emit();
    this.submitted = true;
  }
}
Run Code Online (Sandbox Code Playgroud)


Sho*_*ogg 5

使用模板内部的 .bind 传递带参数的方法

@Component({
  ...
  template: '<child [action]="foo.bind(this, 'someArgument')"></child>',
  ...
})
export class ParentComponent {
  public foo(someParameter: string){
    ...
  }
}

@Component({...})
export class ChildComponent{

  @Input()
  public action: Function; 

  ...
}
Run Code Online (Sandbox Code Playgroud)


Gop*_*hra 5

以下是我在 Angular 13 中的工作(截至 2022 年 3 月)。

\n

PS-这或多或少与其他人的回答相似。添加这个答案只是为了让人们知道它在 Angular 13 中有效。

\n

将函数定义为平面箭头,而不是父组件中的常规函数​​。

\n
callBackFn= (args: string): void => {\n  // callback code here\n  // This will work (Flat Arrow)\n}\n// callbackFn(args: string): void {\n//   //This type of definition will not work.\n// }\n
Run Code Online (Sandbox Code Playgroud)\n

将回调函数作为属性传递给子组件

\n
<app-child [callBack]=\xe2\x80\x9dcallBackFn\xe2\x80\x9d></app-child>\n
Run Code Online (Sandbox Code Playgroud)\n

在子组件中接收回调函数作为输入。该定义应与父级中的定义匹配。

\n
@Input() callBack: (args: string) => void;\n
Run Code Online (Sandbox Code Playgroud)\n

然后在子组件中调用这个函数。您也可以将此称为子组件模板。

\n
this.callBack(\'Test\');\n
Run Code Online (Sandbox Code Playgroud)\n

或者

\n
<button (click)="callBack(\'Test\')"></button>\n
Run Code Online (Sandbox Code Playgroud)\n

但不确定这个方法好不好。\n我在 ReactJS 中看到了类似的方法,它效果很好,但仍然不确定它在 Angular 中是如何工作的以及它的影响是什么。

\n

对这种方法的任何评论将不胜感激。

\n