Comment passer une variable du fichier de modèle de jade à un fichier de script?

119

J'ai des problèmes avec une variable (config) déclarée dans un fichier de modèle jade (index.jade) qui n'est pas passé à un fichier javascript, ce qui fait alors planter mon javascript. Voici le fichier (views / 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')

Voici une partie de mon app.js (côté serveur):

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

Et voici une partie de mon fichier javascript qui plante (public / javascripts / app.js):

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

J'exécute le site en mode développement (NODE_ENV = development) sur localhost (ma propre machine). J'utilise node-inspector pour le débogage, ce qui m'a dit que la variable de configuration n'est pas définie dans public / javascripts / app.js.

Des idées?? Merci!!

Michael Eilers Smith
la source
duplication possible des variables ExpressJS Pass à JavaScript
laggingreflex

Réponses:

174

Il est un peu tard mais ...

script.
  loginName="#{login}";

Cela fonctionne bien dans mon script. Dans Express, je fais ceci:

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

Je suppose que le dernier jade est différent?

Merc.

modifier: ajouté "." après le script pour éviter l'avertissement de Jade.

Merc
la source
1
Merci d'avoir accepté la réponse! Alors, quel était le problème réel avec votre code?
Merc
Oui, cela fonctionne également en enveloppant la variable locale dans le modèle entre guillemets et l'indicateur # {}.
Askdesigners
Cette réponse est dangereuse, la réponse de lagginreflex encode correctement la chaîne (les retours à la ligne dans le nom de connexion pourraient permettre l'exécution de code).
Paul Grove
Donc, le problème était que les guillemets simples étaient attendus, non?
Augustin Riedinger
Pour moi, le problème était le point. Je l'avais à la fin du bloc de script. Avec le point après le mot-clé "script", cela fonctionne comme un charme. Je vous remercie!
Mirko
103

!{}est pour l' interpolation de code sans échappement , qui convient mieux aux objets

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>

L'idée est d'empêcher l'attaquant de:

  1. Sortez de la variable: JSON.stringifyéchappe les guillemets
  2. Sortez de la balise script: si le contenu de la variable (que vous ne pourrez peut-être pas contrôler s'il provient de la base de données par exemple) a une </script>chaîne, l'instruction replace s'en chargera

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


#{}est pour l' interpolation de chaîne échappée , qui ne convient que si vous travaillez avec des chaînes. Cela ne fonctionne pas avec les objets

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

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>
réflexe de retard
la source
1
Excellente réponse! Je recherche ce sujet depuis plus de 20 minutes et aucune autre réponse n'a fait la différence entre! {} Et # {}. Pendant tout ce temps, j'ai juste pensé! {] Était l'équivalent obsolète de # {} ... Merci encore.
Patrick E.
Jusqu'au sommet! Très bonne réponse.
Scarysize le
Excellente réponse, et cela fonctionne pour les objets! Btw, est JSONun objet global? Cela semblait fonctionner sans importer d'autres bibliothèques. Si oui, qu'en est-il de la prise en charge du navigateur?
chharvey
@chharvey JSON est un langage javascript intégré (comme Date ou Math), tous les navigateurs le prennent en charge.
laggingreflex
1
D'accord, c'est la meilleure réponse, mais je ne pense pas que ce soit le bon endroit pour échapper à des chaînes potentiellement dangereuses. IMO, il serait préférable d'omettre l' replaceappel dans ce code et de traiter cette valeur comme toute autre entrée. Il est garanti que JSON.stringify produira du javascript valide (ou échouera), et une fois que vous avez cette valeur potentiellement dangereuse en mémoire, vous pouvez vous assurer de la nettoyer si nécessaire avant de l'utiliser.
Riley Lark
9

Dans mon cas, j'essayais de passer un objet dans un modèle via une route express (semblable à la configuration des OP). Ensuite, j'ai voulu passer cet objet dans une fonction que j'appelais via une balise de script dans un modèle carlin. Bien que la réponse de lagginreflex m'ait rapproché, j'ai fini avec ce qui suit:

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

Cela garantissait que l'objet était passé comme prévu, plutôt que de devoir désérialiser dans la fonction. De plus, les autres réponses semblaient bien fonctionner avec les primitives, mais lorsque des tableaux, etc. étaient passés avec l'objet, ils étaient analysés comme des valeurs de chaîne.

Kyle G
la source
7

Si vous êtes comme moi et que vous utilisez beaucoup cette méthode pour passer des variables, voici une solution de code sans écriture.

Dans votre route node.js, passez les variables dans un objet appelé window, comme ceci:

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

Ensuite, dans votre fichier de mise en page pug / jade (juste avant le block content), vous les sortez comme ceci:

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

Comme mon fichier layout.pug est chargé avec chaque fichier pug, je n'ai pas besoin d '«importer» mes variables encore et encore.

De cette façon, toutes les variables / objets passés à window'comme par magie' se retrouvent dans l' windowobjet réel de votre navigateur où vous pouvez les utiliser dans Reactjs, Angular, ... ou javascript vanilla.

Sam
la source
1
C'est la meilleure réponse que j'ai vue.
Tohir
3

Voici comment j'ai abordé ce problème (en utilisant un dérivé MEAN)

Mes variables:

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

Je devais d'abord m'assurer que les variables de configuration nécessaires étaient passées. MEAN utilise le package node nconf et est configuré par défaut pour limiter les variables transmises par l'environnement. J'ai dû y remédier:

config / config.js:

original:

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

après modifications:

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

Maintenant, je peux définir mes variables comme ceci:

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

Remarque: j'ai réinitialisé "l'indicateur d'hérarchie" sur "__" (double tiret bas) car sa valeur par défaut était ":", ce qui rend les variables plus difficiles à définir à partir de bash. Voir un autre article sur ce fil.

Maintenant, la partie jade: Ensuite, les valeurs doivent être rendues, afin que javascript puisse les récupérer côté client. Une manière simple d'écrire ces valeurs dans le fichier d'index. Comme il s'agit d'une application d'une page (angulaire), cette page est toujours chargée en premier. Je pense que idéalement, cela devrait être un fichier d'inclusion javascript (juste pour garder les choses propres), mais c'est bon pour une démo.

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

app / views / 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};

Dans mon html rendu, cela me donne un joli petit script:

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

À partir de là, il est facile pour angular d'utiliser le code.

Michael
la source
C'est une très longue façon de dire ce que dit déjà la réponse acceptée. De plus, mentionner MEAN, nconf, etc. n'est pas pertinent. La question est de savoir comment passer des variables au client, pas comment vous avez fait votre pile de développement. Pas de vote négatif cependant, puisque vous avez encore mis beaucoup d'efforts dans cette réponse.
Mrchief
trop compliqué
andygoestohollywood
2

Voir cette question: JADE + EXPRESS: Itération sur un objet dans le code JS en ligne (côté client)?

J'ai le même problème. Jade ne transmet pas de variables locales (ou ne fait aucun modèle) aux scripts javascript, il passe simplement le bloc entier sous forme de texte littéral. Si vous utilisez les variables locales «adresse» et «port» dans votre fichier Jade au-dessus de la balise de script, elles devraient apparaître.

Les solutions possibles sont listées dans la question à laquelle j'ai lié ci-dessus, mais vous pouvez soit: - passer chaque ligne en tant que texte sans échappement (! = Au début de chaque ligne), et simplement mettre "-" avant chaque ligne de javascript qui utilise un variable locale, ou: - Passer des variables via un élément dom et accéder via JQuery (moche)

N'y a-t-il pas de meilleur moyen? Il semble que les créateurs de Jade ne veulent pas de support javascript multiligne, comme le montre ce fil dans GitHub: https://github.com/visionmedia/jade/pull/405

Varun Singh
la source