Wie übergebe ich eine Variable aus einer Jade-Vorlagendatei an eine Skriptdatei?

119

Ich habe Probleme mit einer Variablen (config), die in einer Jade-Vorlagendatei (index.jade) deklariert ist und nicht an eine Javascript-Datei übergeben wird, wodurch mein Javascript abstürzt. Hier ist die Datei (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')

Hier ist ein Teil meiner app.js (Serverseite):

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

Und hier ist ein Teil meiner Javascript-Datei, der abstürzt (public / javascripts / app.js):

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

Ich führe die Site im Entwicklungsmodus (NODE_ENV = Entwicklung) auf localhost (meinem eigenen Computer) aus. Ich verwende Node-Inspector zum Debuggen, was mir sagte, dass die Konfigurationsvariable in public / javascripts / app.js undefiniert ist.

Irgendwelche Ideen?? Vielen Dank!!

Michael Eilers Smith
quelle
Mögliches Duplikat von ExpressJS Pass-Variablen an JavaScript
Laggingreflex

Antworten:

174

Es ist etwas spät, aber ...

script.
  loginName="#{login}";

Dies funktioniert gut in meinem Skript. In Express mache ich das:

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

Ich denke, die neueste Jade ist anders?

Merc.

bearbeiten: "." hinzugefügt nach dem Skript, um eine Jade-Warnung zu verhindern.

Merc
quelle
1
Vielen Dank für die Annahme der Antwort! Was war das eigentliche Problem mit Ihrem Code?
Merc
Ja, das hat auch funktioniert, indem ich die lokale Variable in Anführungszeichen und den Indikator # {} in die Vorlage eingeschlossen habe.
Askdesigners
Diese Antwort ist gefährlich. Die Antwort von lagginreflex codiert die Zeichenfolge korrekt (Zeilenumbrüche im Anmeldenamen können die Codeausführung ermöglichen).
Paul Grove
Das Problem war also, dass nur einfache Anführungszeichen erwartet werden, während doppelte Anführungszeichen erwartet werden, oder?
Augustin Riedinger
Für mich war das Problem der Punkt. Ich hatte es am Ende des Skriptblocks. Mit dem Punkt nach dem Schlüsselwort "script" funktioniert es wie ein Zauber. Danke dir!
Mirko
102

!{}ist für die Interpolation von Code ohne Flucht , die für Objekte besser geeignet ist

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>

Die Idee ist, den Angreifer daran zu hindern:

  1. Aus der Variablen ausbrechen: JSON.stringifyEntkommt den Anführungszeichen
  2. Brechen Sie aus dem Skript-Tag aus: Wenn der Variableninhalt (den Sie möglicherweise nicht steuern können, wenn er beispielsweise aus der Datenbank stammt) eine </script>Zeichenfolge enthält, wird dies durch die replace-Anweisung erledigt

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


#{}ist für die Interpolation von Escapezeichenfolgen gedacht , die nur geeignet ist, wenn Sie mit Zeichenfolgen arbeiten. Es funktioniert nicht mit Objekten

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

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>
Laggingreflex
quelle
1
Hervorragende Antwort! Ich habe dieses Thema über 20 Minuten lang gesucht und keine anderen Antworten haben den Unterschied zwischen! {} Und # {} angegeben. Die ganze Zeit dachte ich nur! {] War das veraltete Äquivalent von # {} ... Nochmals vielen Dank.
Patrick E.
Nach oben! Gute Antwort.
Scarysize
Tolle Antwort, und es funktioniert für Objekte! Übrigens, ist JSONein globales Objekt? Es schien zu funktionieren, ohne andere Bibliotheken zu importieren. Wenn ja, was ist mit der Browserunterstützung?
Chharvey
@chharvey JSON ist eine integrierte Javascript-Sprache (wie Date oder Math), die von allen Browsern unterstützt wird.
Laggingreflex
1
Einverstanden ist dies die beste Antwort, aber ich denke nicht, dass dies der richtige Ort ist, um potenziell gefährlichen Saiten zu entkommen. IMO ist es besser, den replaceAufruf in diesem Code wegzulassen und diesen Wert wie jede andere Eingabe zu behandeln. JSON.stringify erzeugt garantiert gültiges Javascript (oder schlägt fehl). Nachdem Sie diesen potenziell gefährlichen Wert im Speicher haben, können Sie sicherstellen, dass Sie ihn vor der Verwendung nach Bedarf bereinigen.
Riley Lark
9

In meinem Fall habe ich versucht, ein Objekt über eine Expressroute (ähnlich dem OP-Setup) in eine Vorlage zu übergeben. Dann wollte ich dieses Objekt an eine Funktion übergeben, die ich über ein Skript-Tag in einer Mops-Vorlage aufrief. Obwohl mich die Antwort von Lagginreflex nahe gebracht hat, kam ich zu folgendem Ergebnis :

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

Dies stellte sicher, dass das Objekt wie erwartet übergeben wurde, anstatt die Funktion deserialisieren zu müssen. Auch die anderen Antworten schienen mit Grundelementen gut zu funktionieren, aber wenn Arrays usw. zusammen mit dem Objekt übergeben wurden, wurden sie als Zeichenfolgenwerte analysiert.

Kyle G.
quelle
7

Wenn Sie wie ich sind und diese Methode zum Übergeben von Variablen häufig verwenden, finden Sie hier eine Lösung ohne Code.

Übergeben Sie in Ihrer node.js-Route die Variablen in einem Objekt window, das wie folgt aufgerufen wird:

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

Dann erhalten Sie sie in Ihrer Mops / Jade-Layoutdatei (kurz vor dem block content) wie folgt heraus:

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

Da meine layout.pug-Datei mit jeder Mops-Datei geladen wird, muss ich meine Variablen nicht immer wieder "importieren".

Auf diese Weise windowlanden alle Variablen / Objekte, die an 'magisch' übergeben wurden, im realen windowObjekt Ihres Browsers, wo Sie sie in Reactjs, Angular, ... oder Vanille-Javascript verwenden können.

Sam
quelle
1
Dies ist die beste Antwort, die ich gesehen habe.
Tohir
3

Hier ist, wie ich dies angesprochen habe (unter Verwendung eines MEAN-Derivats)

Meine Variablen:

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

Zuerst musste ich sicherstellen, dass die erforderlichen Konfigurationsvariablen übergeben wurden. MEAN verwendet das Knoten-Nconf-Paket und ist standardmäßig so eingerichtet, dass begrenzt wird, welche Variablen von der Umgebung übergeben werden. Ich musste Abhilfe schaffen:

config / config.js:

Original:

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

nach Änderungen:

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

Jetzt kann ich meine Variablen folgendermaßen einstellen:

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

Hinweis: Ich habe den "Heirarchy Indicator" auf "__" (doppelter Unterstrich) zurückgesetzt, da der Standardwert ":" war, was das Setzen von Variablen aus Bash erschwert. Siehe einen anderen Beitrag zu diesem Thema.

Jetzt der Jadeteil: Als nächstes müssen die Werte gerendert werden, damit Javascript sie auf der Clientseite aufnehmen kann. Eine einfache Möglichkeit, diese Werte in die Indexdatei zu schreiben. Da es sich um eine einseitige App (eckig) handelt, wird diese Seite immer zuerst geladen. Ich denke, im Idealfall sollte dies eine Javascript-Include-Datei sein (nur um die Dinge sauber zu halten), aber dies ist gut für eine Demo.

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

In meinem gerenderten HTML gibt mir dies ein schönes kleines Skript:

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

Ab diesem Punkt ist es für Angular einfach, den Code zu verwenden.

Michael
quelle
Dies ist eine sehr langwierige Art zu sagen, was die akzeptierte Antwort bereits sagt. Darüber hinaus ist die Erwähnung von MEAN, nconf usw. irrelevant. Die Frage ist, wie Variablen an den Client übergeben werden, nicht wie Sie Ihren Dev-Stack erstellt haben. Kein Downvoting, da Sie sich in dieser Antwort immer noch viel Mühe gegeben haben.
Mrchief
übermäßig kompliziert
andygoestohollywood
2

Siehe diese Frage: JADE + EXPRESS: Iterieren über Objekt in Inline-JS-Code (clientseitig)?

Ich habe das gleiche Problem. Jade übergibt keine lokalen Variablen an Javascript-Skripte (oder erstellt überhaupt keine Vorlagen), sondern übergibt einfach den gesamten Block als Literaltext. Wenn Sie die lokalen Variablen 'address' und 'port' in Ihrer Jade-Datei über dem Skript-Tag verwenden, sollten sie angezeigt werden.

Mögliche Lösungen sind in der Frage aufgeführt, auf die ich oben verwiesen habe. Sie können jedoch entweder: - jede Zeile als uneingeschränkten Text (! = Am Anfang jeder Zeile) übergeben und einfach "-" vor jede Zeile von Javascript setzen, die a verwendet lokale Variable oder: - Variablen über ein dom-Element übergeben und über JQuery zugreifen (hässlich)

Gibt es keinen besseren Weg? Es scheint, dass die Entwickler von Jade keine mehrzeilige Javascript-Unterstützung wünschen, wie dieser Thread in GitHub zeigt: https://github.com/visionmedia/jade/pull/405

Varun Singh
quelle