¿Cómo crear variables globales accesibles en todas las vistas usando Express / Node.JS?

81

Ok, he creado un blog usando Jekyll y puedes definir variables en un archivo _config.ymlque son accesibles en todas las plantillas / diseños. Actualmente estoy usando Node.JS / Express con plantillas EJS y ejs-locals (para parciales / diseños. Estoy buscando hacer algo similar a las variables globales como las site.titleque se encuentran _config.ymlsi alguien está familiarizado con Jekyll. Tengo variables como el título del sitio (en lugar del título de la página), el nombre del autor / empresa, que permanece igual en todas mis páginas.

Aquí hay un ejemplo de lo que estoy haciendo actualmente:

exports.index = function(req, res){
    res.render('index', { 
        siteTitle: 'My Website Title',
        pageTitle: 'The Root Splash Page',
        author: 'Cory Gross',
        description: 'My app description',
        indexSpecificData: someData
    });
};

exports.home = function (req, res) {
    res.render('home', {
        siteTitle: 'My Website Title',
        pageTitle: 'The Home Page',
        author: 'Cory Gross',
        description: 'My app description',
        homeSpecificData: someOtherData
    });
};

Me gustaría poder definir variables como el título, la descripción, el autor, etc. de mi sitio en un solo lugar y tenerlas accesibles en mis diseños / plantillas a través de EJS sin tener que pasarlas como opciones a cada llamada a res.render. ¿Hay alguna manera de hacer esto y todavía me permite pasar otras variables específicas para cada página?

Cory Gross
fuente

Respuestas:

109

Después de tener la oportunidad de estudiar un poco más la referencia de la API de Express 3 , descubrí lo que estaba buscando. Específicamente, las entradas para app.localsy luego un poco más abajo res.localscontenían las respuestas que necesitaba.

Descubrí por mí mismo que la función app.localstoma un objeto y almacena todas sus propiedades como variables globales dentro del ámbito de la aplicación. Estos globales se pasan como variables locales a cada vista. La función res.locals, sin embargo, está en el ámbito de la solicitud y, por tanto, las variables de respuesta local son accesibles sólo a la vista (s) prestados durante esa particular de petición / respuesta.

Entonces, para mi caso, app.jslo que hice fue agregar:

app.locals({
    site: {
        title: 'ExpressBootstrapEJS',
        description: 'A boilerplate for a simple web application with a Node.JS and Express backend, with an EJS template with using Twitter Bootstrap.'
    },
    author: {
        name: 'Cory Gross',
        contact: '[email protected]'
    }
});

A continuación, todas estas variables son accesibles en mis puntos de vista como site.title, site.description, author.name, author.contact.

También podría definir variables locales para cada respuesta a una solicitud con res.locals, o simplemente pasar variables como el título de la página como optionsparámetro en la renderllamada.

EDITAR: este método no le permitirá utilizar estos locales en su middleware. De hecho, me encontré con esto como sugiere Pickels en el comentario a continuación. En este caso, deberá crear una función de middleware como tal en su respuesta alternativa (y apreciada). Su función de middleware deberá agregarlos res.localspara cada respuesta y luego llamar next. Esta función de middleware deberá colocarse por encima de cualquier otro middleware que necesite utilizar estos locales.

EDITAR: Otra diferencia entre declarar locales vía app.localsy res.localses que con app.localslas variables se establecen una sola vez y persisten durante toda la vida de la aplicación. Cuando configura locales res.localsen su middleware, estos se configuran cada vez que recibe una solicitud. Básicamente, debería preferir configurar globales mediante, a app.localsmenos que el valor dependa de la reqvariable de solicitud pasada al middleware. Si el valor no cambia, será más eficiente establecerlo una sola vez en app.locals.

Cory Gross
fuente
1
app.locals tiene más sentido de lo que sugerí. Sin embargo, tiene una trampa, no puede acceder a los locales en su middleware. En este caso, probablemente no importe, pero es una de esas trampas con las que a veces te encuentras.
Pickels
También puede definir la configuración en un archivo js o json y simplemente requerirlo () donde lo necesite. El archivo solo se cargará una vez durante la aplicación, y cada módulo que lo requiera tendrá acceso a los valores que definió. Vea las diversas respuestas en stackoverflow.com/questions/5869216/…
Joe Lapp
51
En Express 4 locals no es una función, sino un objeto. Para configurar una propiedad:app.locals.site.title = 'Example';
Martti Laine
¡Guauu! Ahora toda la aplicación se puede configurar a través de un solo archivo. Un disparo limpio. +1
Dipak
@MarttiLaine ¡Gracias amigo!
Ali Emre Çakmakoğlu
55

Puede hacer esto agregándolos al objeto locals en un middleware general.

app.use(function (req, res, next) {
   res.locals = {
     siteTitle: "My Website's Title",
     pageTitle: "The Home Page",
     author: "Cory Gross",
     description: "My app's description",
   };
   next();
});

Locals también es una función que extenderá el objeto locals en lugar de sobrescribirlo. Entonces lo siguiente también funciona

res.locals({
  siteTitle: "My Website's Title",
  pageTitle: "The Home Page",
  author: "Cory Gross",
  description: "My app's description",
});

Ejemplo completo

var app = express();

var middleware = {

    render: function (view) {
        return function (req, res, next) {
            res.render(view);
        }
    },

    globalLocals: function (req, res, next) {
        res.locals({ 
            siteTitle: "My Website's Title",
            pageTitle: "The Root Splash Page",
            author: "Cory Gross",
            description: "My app's description",
        });
        next();
    },

    index: function (req, res, next) {
        res.locals({
            indexSpecificData: someData
        });
        next();
    }

};


app.use(middleware.globalLocals);
app.get('/', middleware.index, middleware.render('home'));
app.get('/products', middleware.products, middleware.render('products'));

También agregué un middleware de renderizado genérico. De esta manera, no tiene que agregar res.render a cada ruta, lo que significa que tiene una mejor reutilización del código. Una vez que siga la ruta del middleware reutilizable, notará que tendrá muchos bloques de construcción que acelerarán enormemente el desarrollo.

Pepinillos
fuente
Sé que esta es una respuesta antigua, pero los parámetros de su globalLocalsfunción para reqy resestán al revés.
brandon927
¿Puedo consultar datos destinados a ser globales, de esta manera, por ejemplo, datos de la barra de navegación que se cargan directamente desde la base de datos cada solicitud de página? Parece ser que esta idea podría sobrepasar el uso de los locales, por ejemplo, una llamada de mangosta. Estoy buscando carga directa, en cada ruta, ya que la barra de navegación es global, buscaré el almacenamiento en caché más tarde. Alternativamente, tal vez necesite una función que se ejecute una vez y luego las cargue a los locales después de recopilar los datos.
culpar al
1
res.localsya no parece ser una función.
killjoy
Utilizo su enfoque para mostrar algunos productos en el pie de página. ¡Gracias!
Carnaru Valentin
18

Para Express 4.0 descubrí que el uso de variables de nivel de aplicación funciona de manera un poco diferente y la respuesta de Cory no funcionó para mí.

De los documentos: http://expressjs.com/en/api.html#app.locals

Descubrí que puedes declarar una variable global para la aplicación en

app.locals

p.ej

app.locals.baseUrl = "http://www.google.com"

Y luego, en su aplicación puede acceder a estas variables y en su middleware express puede acceder a ellas en el objeto req como

req.app.locals.baseUrl

p.ej

console.log(req.app.locals.baseUrl)
//prints out http://www.google.com
RamRovi
fuente
1
Además, el servidor expreso debe reiniciarse cuando se agregan nuevos locales en el código.
Wtower
14

En su app.js necesita agregar algo como esto

global.myvar = 100;

Ahora, en todos sus archivos que desee utilizar esta variable, puede acceder a ella como myvar

Fernando Bustos
fuente
Estoy usando require módulos, y tuve que hacer referencia a ellos como en global.myvartodas partes, y eso funcionó. Esta fue una solución fácil y agradable para socket.io, donde quiero emitir mensajes de controladores en otros archivos. Puse mi objeto "io" global.myIOy todo funciona muy bien.
Thom Porter
6
En realidad, esto no se recomienda ni se considera una buena práctica en el desarrollo de Node. Node.JS incluye una implementación de sistema de módulos basada en el sistema CommonJS y está completamente centrado en la idea de módulos que no comparten un alcance global, sino que utilizan el método require. Esto también establece una var js global en Node en lugar de una local para Express view / templates como pide la pregunta.
Cory Gross
@CoryGross ¿Es una buena práctica establecer el objeto de solicitud como una variable global como esta?
Ali Sherafat
Solo esto funcionó. ¡Gracias Sr. F! @CoryGross ¿cómo dejo que todos los archivos de mi aplicación accedan a un solo objeto?
Divij Sehgal
Esta es la mejor respuesta. Significa que el objeto global está disponible para pasar a una vista o en cualquier lugar que desee. De global.cdnURL = "https://cdn.domain.comesa manera, puede pasar a una vista para cargar contenido estático.
volumen uno
1

Una forma de hacerlo actualizando la app.localsvariable de esa aplicación enapp.js

Establecer a través de seguir

var app = express();
app.locals.appName = "DRC on FHIR";

Tener acceso

app.listen(3000, function () {
    console.log('[' + app.locals.appName + '] => app listening on port 3001!');
});

Elaborado con una captura de pantalla del ejemplo de @RamRovi con una ligera mejora.

ingrese la descripción de la imagen aquí

Gajen Sunthara
fuente
1

también puedes usar "global"

Ejemplo:

declarar así:

  app.use(function(req,res,next){
      global.site_url = req.headers.host;   // hostname = 'localhost:8080'
      next();
   });

Úselo así: en cualquier vista o archivo ejs <% console.log (site_url); %>

en archivos js console.log (site_url);

Shaik Matheen
fuente
0

Con las diferentes respuestas, implementé este código para usar un archivo externo JSON cargado en "app.locals"

Parámetros

{
    "web": {
        "title" : "Le titre de ma Page",
        "cssFile" : "20200608_1018.css"
    }
}

Solicitud

var express     = require('express');
var appli       = express();
var serveur     = require('http').Server(appli);

var myParams    = require('./include/my_params.json');
var myFonctions = require('./include/my_fonctions.js');

appli.locals = myParams;

Página EJS

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title><%= web.title %></title>
    <link rel="stylesheet" type="text/css" href="/css/<%= web.cssFile %>">
</head>

</body>
</html>

Esperando que ayude

Juan - 6510866
fuente
-1

Lo que hago para evitar tener un alcance global contaminado es crear un script que pueda incluir en cualquier lugar.

// my-script.js
const ActionsOverTime = require('@bigteam/node-aot').ActionsOverTime;
const config = require('../../config/config').actionsOverTime;
let aotInstance;

(function () {
  if (!aotInstance) {
    console.log('Create new aot instance');
    aotInstance = ActionsOverTime.createActionOverTimeEmitter(config);
  }
})();

exports = aotInstance;

Hacer esto solo creará una nueva instancia una vez y la compartirá en todos los lugares donde se incluye el archivo. No estoy seguro si se debe a que la variable está almacenada en caché o debido a un mecanismo de referencia interno para la aplicación (que podría incluir el almacenamiento en caché). Cualquier comentario sobre cómo el nodo resuelve esto sería genial.

Tal vez también lea esto para obtener la esencia de cómo funciona require: http://fredkschott.com/post/2014/06/require-and-the-module-system/

Dewwwald
fuente