¿Cómo pasar la variable del archivo de plantilla de jade a un archivo de script?

119

Tengo problemas con una variable (config) declarada en un archivo de plantilla de jade (index.jade) que no se pasa a un archivo de javascript, lo que hace que mi javascript se bloquee. Aquí está el archivo (vistas / index.jade):

h1 #{title}

script(src='./socket.io/socket.io.js')
script(type='text/javascript')
  var config = {};
  config.address = '#{address}';
  config.port = '#{port}';
script(src='./javascripts/app.js')

Aquí hay una parte de mi app.js (lado del servidor):

  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.set('address', 'localhost');
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', function(req, res){
  res.render('index', {
    address: app.settings.address,
    port: app.settings.port
});
});

if (!module.parent) {
  app.listen(app.settings.port);
  console.log("Server listening on port %d",
app.settings.port);
}

// Start my Socket.io app and pass in the socket
require('./socketapp').start(io.listen(app));

Y aquí hay una parte de mi archivo javascript que falla (public / javascripts / app.js):

(function() {
        var socket = new io.Socket(config.address, {port: config.port, rememberTransport: false});

Estoy ejecutando el sitio en modo de desarrollo (NODE_ENV = desarrollo) en localhost (mi propia máquina). Estoy usando el inspector de nodos para la depuración, lo que me dijo que la variable de configuración no está definida en public / javascripts / app.js.

¿¿Algunas ideas?? ¡¡Gracias!!

Michael Eilers Smith
fuente
posible duplicado de ExpressJS Pass variables a JavaScript
laggingreflex

Respuestas:

174

Es un poco tarde pero ...

script.
  loginName="#{login}";

Esto está funcionando bien en mi guión. En Express, estoy haciendo esto:

exports.index = function(req, res){
  res.render( 'index',  { layout:false, login: req.session.login } );
};

¿Supongo que el último jade es diferente?

Merc.

editar: agregado "." después de la secuencia de comandos para evitar la advertencia de Jade.

Merc
fuente
1
¡Gracias por aceptar la respuesta! Entonces, ¿cuál fue el problema real con su código?
Merc
Sí, esto también funcionó al ajustar la variable local en la plantilla entre comillas y el indicador # {}.
Askdesigners
Esta respuesta es peligrosa, la respuesta de lagginreflex codifica correctamente la cadena (las nuevas líneas en el nombre de inicio de sesión podrían permitir la ejecución del código).
Paul Grove
Entonces, el problema era solo las comillas simples, mientras que se esperaban dobles, ¿verdad?
Augustin Riedinger
Para mí el problema fue el punto. Lo tenía al final del bloque de script. Con el punto después de la palabra clave "script" funciona como un encanto. ¡Gracias!
Mirko
102

!{}es para la interpolación de código sin escape , que es más adecuado para objetos

script var data = !{JSON.stringify(data).replace(/<\//g, '<\\/')}

{ foo: 'bar' }
// becomes:
<script>var data = {"foo":"bar"}</script>

{ foo: 'bar</script><script>alert("xss")//' }
// becomes:
<script>var data = {"foo":"bar<\/script><script>alert(\"xss\")//"}</script>

La idea es evitar que el atacante:

  1. Salir de la variable: JSON.stringifyescapa de las comillas
  2. Salga de la etiqueta del script: si el contenido de la variable (que es posible que no pueda controlar si proviene de la base de datos, por ejemplo) tiene una </script>cadena, la declaración de reemplazo se encargará de ello

https://github.com/pugjs/pug/blob/355d3dae/examples/dynamicscript.pug


#{}es para la interpolación de cadenas de escape , que es adecuada solo si está trabajando con cadenas. No funciona con objetos

script var data = #{JSON.stringify(data)}

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>
reflejo
fuente
1
¡Excelente respuesta! He estado buscando este tema durante más de 20 minutos y ninguna otra respuesta indicó la diferencia entre! {} Y # {}. Todo este tiempo pensé que! {] Era el equivalente obsoleto de # {} ... Gracias nuevamente.
Patrick E.
¡A la cima! Gran respuesta.
Scarysize
Gran respuesta, ¡y funciona para objetos! Por cierto, ¿es JSONun objeto global? Parecía funcionar sin importar ninguna otra biblioteca. Si es así, ¿qué pasa con la compatibilidad con el navegador?
chharvey
@chharvey JSON es un lenguaje javascript incorporado (como Date o Math), todos los navegadores lo admiten.
laggingreflex
1
De acuerdo, esta es la mejor respuesta, pero no creo que este sea el lugar adecuado para escapar de cadenas potencialmente peligrosas. En mi opinión, sería mejor omitir la replacellamada en este código y tratar este valor como cualquier otra entrada. Se garantiza que JSON.stringify producirá un javascript válido (o fallará), y una vez que tenga este valor potencialmente peligroso en la memoria, puede asegurarse de desinfectarlo según sea necesario antes de usarlo.
Riley Lark
9

En mi caso, estaba intentando pasar un objeto a una plantilla a través de una ruta rápida (similar a la configuración de OP). Luego quise pasar ese objeto a una función que estaba llamando a través de una etiqueta de script en una plantilla pug. Aunque la respuesta de lagginreflex me acercó, terminé con lo siguiente:

script.
    var data = JSON.parse('!{JSON.stringify(routeObj)}');
    funcName(data)

Esto aseguró que el objeto se pasara como se esperaba, en lugar de necesitar deserializar en la función. Además, las otras respuestas parecían funcionar bien con primitivas, pero cuando se pasaban matrices, etc. junto con el objeto, se analizaban como valores de cadena.

Kyle G
fuente
7

Si eres como yo y usas mucho este método de pasar variables, aquí tienes una solución de código sin escritura.

En su ruta node.js, pase las variables en un objeto llamado window, así:

router.get('/', function (req, res, next) {
    res.render('index', {
        window: {
            instance: instance
        }
    });
});

Luego, en su archivo de diseño de pug / jade (justo antes del block content), los saca así:

if window
    each object, key in window
        script.
            window.!{key} = !{JSON.stringify(object)};

Como mi archivo layout.pug se carga con cada archivo pug, no necesito 'importar' mis variables una y otra vez.

De esta manera, todas las variables / objetos pasados ​​a window'mágicamente' terminan en el windowobjeto real de su navegador donde puede usarlos en Reactjs, Angular, ... o vainilla javascript.

Sam
fuente
1
Esta es la mejor respuesta que he visto.
Tohir
3

Así es como abordé esto (usando un derivado MEAN)

Mis variables:

{
  NODE_ENV : development,
  ...
  ui_varables {
     var1: one,
     var2: two
  }
}

Primero tenía que asegurarme de que se pasaran las variables de configuración necesarias. MEAN usa el paquete node nconf y, de forma predeterminada, está configurado para limitar qué variables se pasan del entorno. Tuve que remediar eso:

config / config.js:

original:

nconf.argv()
  .env(['PORT', 'NODE_ENV', 'FORCE_DB_SYNC'] ) // Load only these environment variables
  .defaults({
  store: {
    NODE_ENV: 'development'
  }
});

después de modificaciones:

nconf.argv()
  .env('__') // Load ALL environment variables
  // double-underscore replaces : as a way to denote hierarchy
  .defaults({
  store: {
    NODE_ENV: 'development'
  }
});

Ahora puedo configurar mis variables así:

export ui_varables__var1=first-value
export ui_varables__var2=second-value

Nota: Restablecí el "indicador de jerarquía" a "__" (guión bajo doble) porque su valor predeterminado era ":", lo que hace que las variables sean más difíciles de configurar desde bash. Vea otra publicación en este hilo.

Ahora la parte de jade: a continuación, se deben representar los valores, para que JavaScript pueda recogerlos en el lado del cliente. Una forma sencilla de escribir estos valores en el archivo de índice. Debido a que esta es una aplicación de una página (angular), esta página siempre se carga primero. Creo que idealmente debería ser un archivo de inclusión javascript (solo para mantener las cosas limpias), pero esto es bueno para una demostración.

app / controllers / index.js:

'use strict';
var config = require('../../config/config');

exports.render = function(req, res) {
  res.render('index', {
    user: req.user ? JSON.stringify(req.user) : "null",
    //new lines follow:
    config_defaults : {
       ui_defaults: JSON.stringify(config.configwriter_ui).replace(/<\//g, '<\\/')  //NOTE: the replace is xss prevention
    }
  });
};

aplicación / vistas / index.jade:

extends layouts/default

block content
  section(ui-view)
    script(type="text/javascript").
    window.user = !{user};
    //new line here
    defaults = !{config_defaults.ui_defaults};

En mi html renderizado, esto me da un pequeño script agradable:

<script type="text/javascript">
 window.user = null;         
 defaults = {"var1":"first-value","var2:"second-value"};
</script>        

Desde este punto es fácil para angular utilizar el código.

Miguel
fuente
Esta es una forma muy prolija de decir lo que ya dice la respuesta aceptada. Además, mencionar MEAN, nconf, etc. es irrelevante. La pregunta es sobre cómo pasar variables al cliente, no sobre cómo hizo su pila de desarrollo. Sin embargo, no votando negativamente, ya que todavía se esfuerza mucho en esta respuesta.
Mrchief
demasiado complicado
andygoestohollywood
2

Vea esta pregunta: JADE + EXPRESS: ¿Iterando sobre el objeto en el código JS en línea (lado del cliente)?

Estoy teniendo el mismo problema. Jade no pasa variables locales (ni hace ninguna plantilla) a los scripts de JavaScript, simplemente pasa todo el bloque como texto literal. Si usa las variables locales 'dirección' y 'puerto' en su archivo Jade encima de la etiqueta de script, deberían aparecer.

Las posibles soluciones se enumeran en la pregunta a la que me vinculé anteriormente, pero puede: - pasar cada línea como texto sin escape (! = Al comienzo de cada línea), y simplemente poner "-" antes de cada línea de javascript que use un variable local, o: - Pasar variables a través de un elemento dom y acceder a través de JQuery (feo)

¿No hay mejor manera? Parece que los creadores de Jade no quieren soporte de javascript multilínea, como se muestra en este hilo en GitHub: https://github.com/visionmedia/jade/pull/405

Varun Singh
fuente