web-dev-qa-db-fra.com

Angular transmet la fonction de rappel au composant enfant sous la forme @Input similaire à AngularJS

AngularJS a les paramètres & où vous pouvez passer un callback à une directive (par exemple façon de callback AngularJS . Est-il possible de passer un callback en tant que @Input pour un Angular Composant (quelque chose comme ci-dessous)? Sinon, quelle serait la chose la plus proche de ce que fait 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>
162
Michail Michailidis

Je pense que c'est une mauvaise solution. Si vous souhaitez passer une fonction dans un composant avec @Input(), @Output() décorateur est ce que vous recherchez.

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>
227
Serginho

UPDATE

Cette réponse a été soumise alors que Angular 2 était encore en alpha et que de nombreuses fonctionnalités n'étaient pas disponibles/non documentées. Bien que le programme ci-dessous fonctionne toujours, cette méthode est maintenant totalement obsolète. Je fortement recommande la réponse acceptée par rapport à celle ci-dessous.

Réponse originale

Oui, c’est vrai, mais vous voudrez vous assurer qu’elle est correctement définie. Pour cela, j'ai utilisé une propriété pour m'assurer que this signifie ce que je veux.

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

Une alternative à la réponse donnée par SnareChops.

Vous pouvez utiliser .bind (this) dans votre modèle pour avoir le même effet. Ce n'est peut-être pas aussi propre mais cela économise quelques lignes. Je suis actuellement sur 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; 
  ...
}
26
Max Fahl

Dans certains cas (rares), vous aurez peut-être besoin que la logique métier soit exécutée par un composant parent. Dans l'exemple ci-dessous, nous avons un composant enfant qui rend la ligne d'un tableau en fonction de la logique fournie par le composant parent:

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

Peut-être que ce n'est pas un cas idéal et que cela peut être fait d'une meilleure façon, mais je voulais simplement démontrer deux choses ici:

  1. La grosse flèche (=>) fonctionne au lieu de .bind (this) pour contenir le bon contexte;
  2. Déclaration Typeafe de la fonction de rappel dans le composant enfant.
9
Danylo Zatorsky

Méthode de passage avec argument, utilisant .bind à l'intérieur du modèle

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

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

  @Input()
  public action: Function; 

  ...
}
4
Shogg

Par exemple, j'utilise une fenêtre modale de connexion, où la fenêtre modale est le parent, le formulaire de connexion est l'enfant et le bouton de connexion rappelle la fonction de fermeture du parent modal.

Le modal parent contient la fonction permettant de fermer le modal. Ce parent transmet la fonction de fermeture au composant enfant de connexion.

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

Une fois que le composant de connexion enfant a soumis le formulaire de connexion, il ferme le modal parent à l'aide de la fonction de rappel du parent.

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;
  }
}
4
Camilla Kydland