web-dev-qa-db-fra.com

Nodejs, routes express en classes es6

Je veux nettoyer un peu mon projet et maintenant j'essaie d'utiliser les classes es6 pour mes itinéraires. Mon problème est que this est toujours indéfini.

var express = require('express');
var app = express();

class Routes {
    constructor(){
        this.foo = 10
    }

    Root(req, res, next){
        res.json({foo: this.foo}); // TypeError: Cannot read property 'foo' of undefined
    }
}

var routes = new Routes();
app.get('/', routes.Root);
app.listen(8080);
22
Michael Malura
import express from 'express';
const app = express();

class Routes {
    constructor(){
        this.foo = 10
    }

    const Root = (req, res, next) => {
        res.json({foo: this.foo}); // TypeError: Cannot read property 'foo' of undefined
    }
}

const routes = new Routes();
app.get('/', routes.Root);
app.listen(8080);

Voici une réécriture mineure du code, mais comme certaines des réponses ici l'ont souligné, la fonction elle-même lorsqu'elle est référencée comme cela dans la configuration de la route ignore this et doit être liée. Pour contourner cela, au lieu d'écrire des fonctions "normales", il vous suffit d'écrire des fonctions de définition de "grosse flèche" qui se lient automatiquement et vous êtes prêt à partir!

1
Balkana

essayez d'utiliser le code pour épingler this:

app.get('/', routes.Root.bind(routes));

Vous pouvez sortir du passe-partout en utilisant la fonction de soulignement bindAll . Par exemple:

var _ = require('underscore');

// ..

var routes = new Routes();
_.bindAll(routes, 'Root')
app.get('/', routes.Root);

J'ai également constaté que es7 vous permet d'écrire le code d'une manière plus élégante:

class Routes {
    constructor(){
        this.foo = 10
    }

    Root = (req, res, next) => {
        res.json({foo: this.foo});
    }
}

var routes = new Routes();
app.get('/', routes.Root);
15
zag2art

Cela se produit parce que vous avez passé une méthode en tant que fonction autonome à exprimer. Express ne sait rien de la classe dont il provient, donc il ne sait pas quelle valeur utiliser comme this lorsque votre méthode est appelée.

Vous pouvez forcer la valeur de this avec bind.

app.get('/', routes.Root.bind(routes));

Ou vous pouvez utiliser une construction alternative pour gérer les itinéraires. Vous pouvez toujours utiliser de nombreux avantages syntaxiques pour la programmation orientée objet sans classes.

function Routes() {
  const foo = 10;

  return {
    Root(req, res, next) {
      res.json({ foo });
    }
  };
}

const routes = Routes();
app.get('/', routes.Root);
app.listen(8080);
  • Vous n'aurez pas à vous soucier de la valeur de this
  • Peu importe que la fonction soit appelée avec new ou non
  • Vous pouvez éviter la complexité d'appeler bind sur chaque route

Il y a une bonne liste de ressources ici , sur pourquoi les classes ES6 ne sont pas aussi bonnes qu'elles le paraissent.

10
Dan Prince

Ou si vous n'aimez pas lier le contexte par routes, vous pouvez éventuellement le lier aux méthodes dans le constructeur de votre classe lui-même.

Par exemple:

constructor() {
   this.foo = 10;
   this.Root = this.Root.bind(this);
}
3

Nous avons récemment refactorisé tous nos contrôleurs Express pour qu'ils utilisent une classe de contrôleur de base, et nous avons également rencontré ce problème. Notre solution consistait à faire en sorte que chaque contrôleur lie ses méthodes à lui-même en appelant la méthode d'assistance suivante à partir du constructeur:

  /**
   * Bind methods
   */
  bindMethods() {

    //Get methods
    const proto = Object.getPrototypeOf(this);
    const methods = [
      ...Object.getOwnPropertyNames(Controller.prototype),
      ...Object.getOwnPropertyNames(proto),
    ];

    //Bind methods
    for (const method of methods) {
      if (typeof this[method] === 'function') {
        this[method] = this[method].bind(this);
      }
    }
  }

Cela garantit que les deux méthodes de contrôleur parent et toutes les méthodes personnalisées de la classe de contrôleur enfant sont correctement liées (par exemple Foo extends Controller).

1
Adam Reis

Les réponses ci-dessus semblent un peu trop compliquées. Découvrez ce que j'ai fait ici:

class Routes {
  constructor(req, res, next) {
    this.req = req;
    this.res = res;
    this.next = next;
    this.foo = "BAR"
    // Add more data to this. here if you like
  }

  findAll (){
    const {data, res,} = this; // Or just reference the objects directly with 'this'
    // Call functions, do whaterver here...
    // Once you have the right data you can use the res obejct to pass it back down

    res.json ({foo: this.foo}); // Grabs the foo value from the constructor

  }
}

Maintenant, quand il s'agit d'utiliser cette classe, vous pouvez faire quelque chose dans le sens de ceci:

var express = require('express');
var router = express.Router();
var {Routes} = require('./Routes');

router.get('/foo', (req, res, next) => {
  new Routes(req, res, next).findAll();
});

Je séparerais les deux fichiers, de sorte que vous ayez juste besoin de la classe Routes dans votre fichier Router.

J'espère que cela vous a aidé!

1
James111