Как передать переменную из файла шаблона нефрита в файл сценария?

119

У меня проблема с переменной (config), объявленной в файле шаблона jade (index.jade), который не передается в файл javascript, что затем приводит к сбою моего javascript. Вот файл (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')

Вот часть моего app.js (на стороне сервера):

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

И вот часть моего файла javascript, которая вылетает (public / javascripts / app.js):

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

Я запускаю сайт в режиме разработки (NODE_ENV = development) на localhost (моя собственная машина). Я использую инспектор узлов для отладки, который сообщил мне, что переменная конфигурации не определена в public / javascripts / app.js.

Любые идеи?? Спасибо!!

Майкл Эйлерс Смит
источник
возможное дублирование переменных ExpressJS Pass в JavaScript
laggingreflex

Ответы:

174

Это немного поздно , но ...

script.
  loginName="#{login}";

Это отлично работает в моем сценарии. В Express я делаю так:

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

Я думаю, последний нефрит другой?

Merc.

редактировать: добавлено "." после скрипта, чтобы предотвратить предупреждение Jade.

Merc
источник
1
Спасибо, что приняли ответ! Итак, в чем была настоящая проблема с вашим кодом?
Merc
Да, я получил эту работу, заключив локальную переменную в шаблоне в кавычки и указав индикатор # {}.
Askdesigners
Этот ответ опасен, ответ lagginreflex правильно кодирует строку (новые строки в имени входа могут разрешить выполнение кода).
Paul Grove
Итак, проблема заключалась только в одинарных кавычках, а ожидались двойные, верно?
Августин Ридингер
Для меня проблема была в точке. Он у меня был в конце блока сценария. Точка после ключевого слова "script" работает как шарм. Спасибо!
Мирко
103

!{}для неэкранированной интерполяции кода , которая больше подходит для объектов

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>

Идея состоит в том, чтобы помешать злоумышленнику:

  1. Вырваться из переменной: JSON.stringifyизбегает кавычек
  2. Вырваться из тега сценария: если содержимое переменной (которое вы, возможно, не сможете контролировать, если оно поступает из базы данных, например) имеет </script>строку, оператор replace позаботится об этом

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


#{}предназначен для экранированной интерполяции строк , которая подходит, только если вы работаете со строками. Он не работает с объектами

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

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>
laggingreflex
источник
1
Отличный ответ! Я искал эту тему более 20 минут, и ни в одном другом ответе не указана разница между! {} И # {}. Все это время я просто думал! {] Был устаревшим эквивалентом # {} ... Еще раз спасибо.
Patrick E.
Вверх! Отличный ответ.
Scarysize
Отличный ответ, и он работает для объектов! Кстати, JSONэто глобальный объект? Казалось, работает без импорта каких-либо других библиотек. Если да, то как насчет поддержки браузера?
chharvey
@chharvey JSON - это встроенный язык JavaScript (например, Date или Math), его поддерживают все браузеры.
laggingreflex
1
Согласен, это лучший ответ, но я не думаю, что это подходящее место, чтобы избегать потенциально опасных строк. ИМО, было бы лучше опустить replaceвызов в этом коде и рассматривать это значение так же, как и любой другой ввод. JSON.stringify гарантированно создаст допустимый javascript (или завершится с ошибкой), и после того, как у вас есть это потенциально опасное значение в памяти, вы можете убедиться, что очистили его по мере необходимости перед использованием.
Riley Lark
9

В моем случае я пытался передать объект в шаблон с помощью экспресс-маршрута (аналогично настройке OP). Затем я хотел передать этот объект в функцию, которую я вызывал через тег скрипта в шаблоне мопса. Хотя ответ lagginreflex меня сблизил, в итоге я получил следующее:

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

Это гарантировало, что объект был передан, как ожидалось, вместо того, чтобы десериализовать функцию. Кроме того, другие ответы, похоже, отлично работали с примитивами, но когда массивы и т. Д. Передавались вместе с объектом, они анализировались как строковые значения.

Кайл Джи
источник
7

Если вы похожи на меня и часто используете этот метод передачи переменных, вот решение без записи кода.

В своем маршруте node.js передайте переменные в объекте с именем window, например:

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

Затем в файле макета pug / jade (непосредственно перед block content) вы получите их следующим образом:

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

Поскольку мой файл layout.pug загружается с каждым файлом мопса, мне не нужно «импортировать» свои переменные снова и снова.

Таким образом, все переменные / объекты, переданные window«волшебным образом», попадают в реальный windowобъект вашего браузера, где вы можете использовать их в Reactjs, Angular, ... или ванильном javascript.

Сэм
источник
1
Это лучший ответ, который я когда-либо видел.
Тохир
3

Вот как я решил это (используя производную от MEAN)

Мои переменные:

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

Сначала я должен был убедиться, что передаются необходимые переменные конфигурации. MEAN использует пакет node nconf и по умолчанию настроен на ограничение того, какие переменные передаются из среды. Я должен был исправить это:

Config / config.js:

оригинал:

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

после доработок:

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

Теперь я могу установить свои переменные следующим образом:

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

Примечание: я сбрасываю «индикатор иерархии» на «__» (двойное подчеркивание), потому что по умолчанию он был «:», что затрудняет установку переменных из bash. См. Другой пост в этой теме.

Теперь нефритовая часть. Затем необходимо отобразить значения, чтобы javascript мог их подобрать на стороне клиента. Простой способ записать эти значения в индексный файл. Поскольку это одностраничное приложение (angular), эта страница всегда загружается первой. Я думаю, что в идеале это должен быть включаемый файл javascript (чтобы все было в порядке), но это хорошо для демонстрации.

приложение / контроллеры / 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
    }
  });
};

приложение / просмотров / 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};

В моем визуализированном html я получаю приятный небольшой скрипт:

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

С этого момента для angular легко использовать код.

Майкл
источник
Это очень длинный способ сказать то, что уже сказано в принятом ответе. Более того, упоминание MEAN, nconf и т. Д. Не имеет значения. Вопрос в том, как передавать переменные клиенту, а не в том, как вы создавали свой стек разработчика. Однако не голосование против, поскольку вы все еще приложили много усилий, чтобы ответить.
Mrchief
слишком сложно
Andygoestohollywood
2

См. Этот вопрос: JADE + EXPRESS: Итерация по объекту во встроенном коде JS (на стороне клиента)?

У меня такая же проблема. Jade не передает локальные переменные (и не создает никаких шаблонов) в сценарии javascript, он просто передает весь блок как буквальный текст. Если вы используете локальные переменные «адрес» и «порт» в своем Jade-файле над тегом скрипта, они должны появиться.

Возможные решения перечислены в вопросе, на который я ссылался выше, но вы можете: - передать каждую строку как неэкранированный текст (! = В начале каждой строки) и просто поставить «-» перед каждой строкой javascript, в которой используется локальная переменная или: - Передача переменных через элемент dom и доступ через JQuery (уродливо)

Нет лучшего способа? Похоже, что создатели Jade не хотят поддержки многострочного javascript, как показано в этой ветке на GitHub: https://github.com/visionmedia/jade/pull/405

Варун Сингх
источник