web-dev-qa-db-fra.com

Dois-je écrire des méthodes en tant que fonctions de flèche dans la classe d'Angular

Dans Angular, il est techniquement possible d'écrire des méthodes de classe en tant que fonctions de flèche ES2015, mais je n'ai jamais vu quelqu'un le faire. Prenons par exemple ce composant simple:

@Component({
  selector: 'sample'
})
export class SampleComponent {
  arrowFunction = param => {
    // Do something
  };
  normalFunction(param) {
    // Do something
  }
}

Cela fonctionne sans aucun problème. Y a-t-il des différences? Et pourquoi devrais-je ou ne devrais-je pas l'utiliser?

8
Gideon

Les remarques faites dans cette réponse React sont toujours valables dans Angular, dans tout autre framework ou dans Vanilla JavaScript/TypeScript.

Les méthodes de prototype de classe sont ES6, pas les méthodes de flèche de classe. Les méthodes Arrow appartiennent à proposition de champs de classe et ne font pas partie des spécifications existantes. Ils sont implémentés dans TypeScript et peuvent également être transpilés avec Babel.

Il est généralement préférable d'utiliser le prototype method() { ... } que la flèche method = () => { ... } car il est plus flexible.

Rappels

La seule possibilité réelle offerte par la méthode arrow est qu’elle peut être utilisée de manière transparente en tant que rappel:

class Class {
  method = () => { ... }
}

registerCallback(new Class().method);

Si la méthode prototype doit être utilisée comme callback, elle doit également être liée, cette opération doit être effectuée de préférence dans le constructeur:

class Class {
  constructor() {
    this.method = this.method.bind(this);
  }

  method() { ... }
}

registerCallback(new Class().method);

Un décorateur tel que bind-decorator peut être utilisé dans TypeScript et ES Next pour fournir une alternative plus concise à la liaison de méthode dans le constructeur:

import bind from 'bind-decorator';

class Class {
  @bind
  method() { ... }
}

Héritage

La méthode Arrow limite l'utilisation des méthodes arrow par les classes enfants, sinon elles ne seront pas remplacées. Cela crée un problème si une flèche était négligée:

class Parent {
  method = () => { ... }
}

class Child extends Parent {
  method() { ... } // won't override Parent method
}

Il n'est pas possible d'utiliser super.method() dans la classe enfant car super.method fait référence à Parent.prototype.method, qui n'existe pas:

class Parent {
  method = () => { ... }
}

class Child extends Parent {
  method = () => {
    super.method(); // won't work
    ...
  }
}

Mixins

Les méthodes prototypes peuvent être efficacement utilisées dans les mixins. Les mixins sont utiles pour l'héritage multiple ou pour résoudre les problèmes liés à la visibilité de la méthode TypeScript.

Comme la méthode de la flèche n'est pas disponible sur le prototype de classe, il est impossible de l'atteindre de l'extérieur de la classe:

class Parent {
  method = () => { ... }
}

class Child extends OtherParent { ... }
Object.assign(Child.prototype, Parent.prototype) // method won't be copied

Essai

Une caractéristique intéressante fournie par les méthodes prototypes est qu'elles sont accessibles avant l'instanciation de classe. Elles peuvent donc être espionnées ou simulées lors de tests, même si elles sont appelées juste après la construction:

class Class {
  constructor(arg) {
    this.init(arg);
  }

  init(arg) { ... }
}

spyOn(Class.prototype, 'init').and.callThrough();
const object = new Class(1);
expect(object.init).toHaveBeenCalledWith(1);

Ce n'est pas possible quand une méthode est une flèche.

TL; DR: le choix entre les méthodes prototype et classe de flèches semble être une affaire de goût, mais en réalité, l’utilisation de méthodes prototypes est plus clairvoyante. Vous voudrez peut-être généralement éviter les méthodes de classe de flèche, sauf si vous êtes sûr qu'elles ne causeront aucun inconvénient. N'oubliez pas d'utiliser bind sur les méthodes prototypes si vous les transmettez en tant que rappels.

15
estus

Les fonctions de flèche de classe constituent un bon cas d'utilisation lorsque vous souhaitez passer une fonction à un autre composant et enregistrer le contexte du composant actuel dans la fonction.

@Component({

   template:`
        I'm the parent
       <child-component></child-component>

  `
})
export class PerentComponent{

   text= "default text"
   arrowFunction = param => {
    // Do something
    // let's update something in parent component ( this)

    this.text = "Updated by parent, but called by child"
  };
}

@Component({

   template:`
        I'm the child component

  `
})
export class ChildComponent{
   @Input() parentFunction;

   ngOnInit(){
      this.parentFunction.()
   }
}

 <parent-component></parent-component>

Dans l'exemple ci-dessus, child est capable d'appeler la fonction du composant parent et le texte sera correctement mis à jour, comme si je changeais simplement le parent en tant que: 

export class PerentComponent{

   text= "default text"
   arrowFunction (){
    this.text = "This text will never update the parent's text property, because `this` will be child component  "
  };
}
2
Milad

Dans un cas, vous devez vous abstenir d'utiliser les fonctions de flèche si vous devez effectuer une compilation AOT, comme documenté ici

Lors de la configuration d'un module, vous ne pouvez pas utiliser les fonctions de flèche.

❌ NE PAS FAIRE:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Routes, RouterModule } from '@angular/router';

@NgModule({
  imports: [
    BrowserModule,
    RouterModule,
    HttpModule,
    RouterModule.forRoot([], { errorHandler: (err) => console.error(err) })
  ],
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent
  ]
})
export class AppModule {}

✅ FAIRE:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Routes, RouterModule } from '@angular/router';

function errorHandler(err) {
  console.error(err);
}

@NgModule({
  imports: [
    BrowserModule,
    RouterModule,
    HttpModule,
    RouterModule.forRoot([], { errorHandler })
  ],
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent
  ]
})
export class AppModule {}
1
Andrei Matracaru