Jak przekazać zmienną z pliku szablonu jade do pliku skryptu?

119

Mam problem ze zmienną (config) zadeklarowaną w pliku szablonu Jade (index.jade), która nie jest przekazywana do pliku javascript, co powoduje awarię mojego javascript. Oto plik (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')

Oto część mojego pliku app.js (po stronie serwera):

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

A oto część mojego pliku javascript, który ulega awarii (public / javascripts / app.js):

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

Prowadzę witrynę w trybie programistycznym (NODE_ENV = programowanie) na hoście lokalnym (mój własny komputer). Używam inspektora węzłów do debugowania, który powiedział mi, że zmienna konfiguracyjna jest niezdefiniowana w public / javascripts / app.js.

Jakieś pomysły?? Dzięki!!

Michael Eilers Smith
źródło
możliwy duplikat zmiennych ExpressJS Pass do JavaScript
laggingreflex

Odpowiedzi:

174

Jest to trochę późno, ale ...

script.
  loginName="#{login}";

To działa dobrze w moim skrypcie. W Express robię to:

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

Myślę, że najnowszy jadeit jest inny?

Merc.

edycja: dodano „.” po skrypcie, aby zapobiec ostrzeżeniu Jade.

Merc
źródło
1
Dziękuję za przyjęcie odpowiedzi! Więc jaki był rzeczywisty problem z twoim kodem?
Merc
Tak, działało to również poprzez zawijanie zmiennej lokalnej w szablonie w cudzysłów i wskaźnik # {}.
Askdesigners,
Ta odpowiedź jest niebezpieczna, odpowiedź lagginreflex poprawnie koduje ciąg (znaki nowej linii w nazwie logowania mogą pozwolić na wykonanie kodu).
Paul Grove
Więc problemem były tylko pojedyncze cudzysłowy, podczas gdy oczekuje się podwójnych, prawda?
Augustin Riedinger
Dla mnie problemem była kropka. Miałem to na końcu bloku skryptu. Kropka po słowie kluczowym „skrypt” działa jak urok. Dziękuję Ci!
Mirko
103

!{}służy do interpolacji kodu bez zmiany znaczenia , co jest bardziej odpowiednie dla obiektów

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>

Chodzi o to, aby uniemożliwić atakującemu:

  1. Break out of the variable: JSON.stringifywymyka się cudzysłowom
  2. Wyrwij się ze znacznika skryptu: jeśli zawartość zmiennej (której możesz nie być w stanie kontrolować, jeśli pochodzi z bazy danych, np.) Zawiera </script>ciąg, zajmie się tym instrukcja replace

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


#{}służy do interpolacji ciągów znaków ucieczki , która jest odpowiednia tylko wtedy, gdy pracujesz z napisami. Nie działa z obiektami

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

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>
laggingreflex
źródło
1
Doskonała odpowiedź! Przeszukuję ten temat przez ponad 20 minut i żadne inne odpowiedzi nie wskazały różnicy między! {} A # {}. Przez cały czas myślałem! {] Był przestarzałym odpowiednikiem # {} ... Jeszcze raz dziękuję.
Patrick E.
Na szczyt! Świetna odpowiedź.
Scarysize
Świetna odpowiedź i działa w przypadku obiektów! Przy okazji, czy jest JSONto obiekt globalny? Wydawało się, że działa bez importowania innych bibliotek. Jeśli tak, co z obsługą przeglądarek?
chharvey
@chharvey JSON to wbudowany język javascript (taki jak Date lub Math), obsługują go wszystkie przeglądarki.
laggingreflex
1
Zgodziłem się, że to najlepsza odpowiedź, ale nie sądzę, że jest to właściwe miejsce na ucieczkę przed potencjalnie niebezpiecznymi strunami. IMO byłoby lepiej pominąć replacewywołanie w tym kodzie i traktować tę wartość tak samo, jak każde inne wejście. JSON.stringify gwarantuje, że wygeneruje prawidłowy skrypt javascript (lub zakończy się niepowodzeniem), a po zapisaniu tej potencjalnie niebezpiecznej wartości w pamięci możesz upewnić się, że przed użyciem ją wyczyścisz.
Riley Lark
9

W moim przypadku próbowałem przekazać obiekt do szablonu drogą ekspresową (podobną do konfiguracji OP). Następnie chciałem przekazać ten obiekt do funkcji, którą wywoływałem za pomocą tagu script w szablonie mopsa. Chociaż odpowiedź lagginreflex zbliżyła mnie do siebie, skończyło się na tym:

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

Dzięki temu obiekt został przekazany zgodnie z oczekiwaniami, zamiast konieczności deserializacji w funkcji. Ponadto inne odpowiedzi wydawały się działać dobrze z prymitywami, ale kiedy tablice itp. Były przekazywane wraz z obiektem, były one analizowane jako wartości łańcuchowe.

Kyle G.
źródło
7

Jeśli jesteś podobny do mnie i często używasz tej metody przekazywania zmiennych, oto rozwiązanie kodujące bez zapisu.

W swojej trasie node.js przekaż zmienne w obiekcie o nazwie window, na przykład:

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

Następnie w pliku układu pug / jade (tuż przed block content) otrzymujesz je w ten sposób:

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

Ponieważ mój plik layout.pug jest ładowany wraz z każdym plikiem pug, nie muszę w kółko „importować” moich zmiennych.

W ten sposób wszystkie zmienne / obiekty przekazane do window„magicznie” windowtrafiają do rzeczywistego obiektu przeglądarki, gdzie można ich używać w Reactjs, Angular, ... lub waniliowym javascript.

Sam
źródło
1
To najlepsza odpowiedź, jaką widziałem.
Tohir
3

Oto, jak sobie z tym poradziłem (używając pochodnej MEAN)

Moje zmienne:

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

Najpierw musiałem się upewnić, że niezbędne zmienne konfiguracyjne zostały przekazane. MEAN używa pakietu node nconf i domyślnie jest skonfigurowany tak, aby ograniczać, które zmienne są przekazywane ze środowiska. Musiałem to naprawić:

config / config.js:

oryginalny:

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

po modyfikacjach:

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

Teraz mogę ustawić moje zmienne w ten sposób:

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

Uwaga: zresetowałem "wskaźnik heirarchii" na "__" (podwójne podkreślenie), ponieważ jego domyślnym było ":", co utrudnia ustawianie zmiennych z basha. Zobacz inny post w tym wątku.

Teraz część jadeitowa: Następnie wartości muszą zostać wyrenderowane, aby javascript mógł je pobrać po stronie klienta. Prosty sposób zapisywania tych wartości w pliku indeksu. Ponieważ jest to aplikacja jednostronicowa (kątowa), ta strona jest zawsze ładowana jako pierwsza. Myślę, że powinien to być plik dołączania javascript (tylko po to, aby zachować porządek), ale jest to dobre dla wersji demonstracyjnej.

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

W moim wyrenderowanym html, otrzymuję ładny mały skrypt:

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

Od tego momentu Angular może łatwo wykorzystać kod.

Michael
źródło
To bardzo rozwlekły sposób wyrażenia tego, co już mówi zaakceptowana odpowiedź. Ponadto wspominanie o MEAN, nconf itp. Jest nieistotne. Pytanie dotyczy sposobu przekazywania zmiennych do klienta, a nie sposobu tworzenia stosu deweloperskiego. Nie głosuj w dół, ponieważ nadal wkładasz dużo wysiłku w tę odpowiedź.
Mrchief
zbyt skomplikowane
andygoestohollywood
2

Zobacz to pytanie: JADE + EXPRESS: Iterowanie po obiekcie w wbudowanym kodzie JS (po stronie klienta)?

Mam ten sam problem. Jade nie przekazuje zmiennych lokalnych (ani w ogóle nie tworzy szablonów) do skryptów javascript, po prostu przekazuje cały blok jako dosłowny tekst. Jeśli używasz lokalnych zmiennych „address” i „port” w pliku Jade nad tagiem skryptu, powinny się one pojawić.

Możliwe rozwiązania są wymienione w pytaniu, do którego utworzyłem łącze powyżej, ale możesz: - przekazać każdą linię jako tekst bez znaku zmiany znaczenia (! = Na początku każdej linii) i po prostu wstawić „-” przed każdą linią javascript używającą znaku zmienna lokalna lub: - Przekaż zmienne przez element dom i dostęp przez JQuery (brzydki)

Czy nie ma lepszego sposobu? Wygląda na to, że twórcy Jade nie chcą obsługi wielowierszowego javascript, jak pokazuje ten wątek w GitHub: https://github.com/visionmedia/jade/pull/405

Varun Singh
źródło