Vyhledávání na webu

Angular předat funkci zpětného volání do podřízené komponenty jako @Input podobně jako v AngularJS


AngularJS má parametry &, kde můžete předat callback direktivě (např. AngularJS způsob callbacků). Je možné předat callback jako @Input pro Angular komponentu (něco jako níže)? Pokud ne, co by bylo nejbližší tomu, co AngularJS dělá?

@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>

Odpověď č. 1

Myslím, že to je špatné řešení. Pokud chcete předat Funkci do komponenty pomocí dekorátoru @Input(), dekorátor @Output() je to, co hledáte.

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>

Odpověď č. 2

AKTUALIZACE

Tato odpověď byla předložena, když Angular 2 byl stále v alfa verzi a mnoho funkcí nebylo dostupných ani zdokumentovaných. I když níže uvedené stále funguje, tato metoda je nyní zcela zastaralá. Důrazně doporučuji přijmout odpověď namísto níže uvedené.

Původní odpověď

Ano, ve skutečnosti je, nicméně budete chtít zajistit, aby bylo správně ohraničeno. K tomu jsem použil vlastnost, abych zajistil, že this znamená to, co chci.

@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; 
  ...
}

Odpověď č. 3

V některých případech může být potřeba, aby obchodní logiku prováděla nadřazená komponenta. V níže uvedeném příkladu máme podřízenou komponentu, která vykresluje řádek tabulky v závislosti na logice poskytnuté nadřazenou komponentou:

@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);
  }
}

Takže jsem tu chtěl ukázat 2 věci:

  1. Funkce s tlustou šipkou (=>) místo .bind(this) pro držení správného kontextu;
  2. Typově bezpečné deklarování zpětné volací funkce v dětské komponentě.

Odpověď č. 4

Alternativa k odpovědi, kterou dal SnareChops.

Ve svém šabloně můžete použít .bind(this), abyste dosáhli stejného efektu. Nemusí to být tak čisté, ale ušetří to pár řádků. Momentálně používám Angular 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; 
  ...
}

Odpověď č. 5

Alternativa k odpovědi, kterou dal Max Fahl.

Můžete definovat callback funkci jako šipkovou funkci v rodičovské komponentě, takže ji nebude nutné bindovat.

@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; 
  ...
}

Související články