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


119

У мене виникають проблеми зі змінною (конфігурацією), оголошеною у файлі шаблону нефриту (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 = розробка) на localhost (моя власна машина). Я використовую node-inspector для налагодження, який сказав мені, що змінна config не визначена в public / javascripts / app.js.

Будь-які ідеї ?? Дякую!!


Відповіді:


174

Це трохи пізно , але ...

script.
  loginName="#{login}";

Це добре працює в моєму сценарії. В Експресі я роблю це:

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

Гадаю, останній нефрит відрізняється?

Merc.

редагувати: додано "." після сценарію для запобігання попередження Джейд.


1
Дякуємо, що прийняли відповідь! Отже, у чому полягала справжня проблема з вашим кодом?
Merc

Так, це я працював також, загорнувши локальну змінну в шаблон у лапки та індикатор # {}.
Askdesigners

Ця відповідь небезпечна, відповідь lagginreflex правильно кодує рядок (нові рядки у імені входу можуть дозволити виконання коду).
Пол Гроув

Отже, питання полягало лише в одиничних котируваннях, тоді як очікуються подвійні, правда?
Августин Рідінгер

Для мене питання було крапкою. У мене це було в кінці блоку сценаріїв. З крапкою після ключового слова "скрипт" він працює як шарм. Дякую!
Мирко

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>рядок, оператор заміни подбає про це

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>

1
Відмінна відповідь! Я шукав цю тему 20+ хвилин, і жодна інша відповідь не вказала різниці між! {} Та # {}. Весь цей час я просто думав! {] Був застарілим еквівалентом # {} ... Ще раз дякую.
Патрік Е.

До вершини! Чудова відповідь.
Скасувати розмір

Чудова відповідь, і це працює на об’єкти! Btw, це JSONглобальний об’єкт? Здавалося, він працює без імпорту будь-яких інших бібліотек. Якщо так, то як щодо підтримки браузера?
chharvey

@chharvey JSON - це вбудована мова JavaScript (наприклад, Date або Math), всі браузери її підтримують.
laggingreflex

1
Погодився, це найкраща відповідь, але я не думаю, що це правильне місце для виходу з потенційно небезпечних струн. IMO, було б краще опустити replaceвиклик у цьому коді та ставитися до цього значення так само, як і до будь-якого іншого вводу. JSON.stringify гарантовано створює дійсний javascript (або виходить з ладу), і після того, як ви матимете це потенційно небезпечне значення в пам’яті, ви можете переконатися в тому, що перед використанням потрібно його дезінфікувати.
Райлі Ларк

9

У моєму випадку я намагався передати об’єкт у шаблон через експрес-маршрут (подібний до налаштування ОП). Тоді я хотів передати цей об’єкт у функцію, яку я викликав через тег скрипту в шаблоні мопсів. Хоча відповідь 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
        }
    });
});

Потім у вашому файлі макета мопс / нефрит (безпосередньо перед block content) ви отримаєте їх таким чином:

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

Оскільки мій файл layout.pug завантажується з кожним файлом мопса, мені не потрібно «імпортувати» свої змінні знову і знову.

Таким чином, всі змінні / об'єкти, передані window"магічним чином", опиняються в реальному windowоб'єкті вашого браузера, де ви можете використовувати їх у jaavascript Reactjs, Angular, ... або vanilla.


1
Це найкраща відповідь, яку я бачив.
Тохір

3

Ось як я вирішив це (використовуючи похідну MEAN)

Мої змінні:

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

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

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 міг підібрати їх на стороні клієнта. Простий спосіб записати ці значення в індексний файл. Оскільки це односторінковий додаток (кутовий), ця сторінка завжди завантажується спочатку. Я думаю, що в ідеалі це повинен бути файл з включенням 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
    }
  });
};

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

У моєму наданому HTML-коді це дає мені гарний маленький сценарій:

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

З цього моменту для кутового легко використовувати код.


Це дуже довго звивистий спосіб сказати те, що вже прийнята відповідь. Більше того, згадування MEAN, nconf тощо не має значення. Питання полягає в тому, як передавати змінні клієнту, а не про те, як ви зробили стек свого розробника. Однак не зволікаючи, оскільки ви все ще доклали багато зусиль у цій відповіді.
Mrchief

надмірно складний
andygoestohollywood

2

Дивіться це питання: JADE + EXPRESS: Ітерація над об'єктом вбудованого коду JS (на стороні клієнта)?

У мене така ж проблема. Нефрит не передає локальні змінні (або взагалі не якісь шаблони) у сценарії javascript, він просто передає весь блок як буквальний текст. Якщо ви використовуєте локальні змінні 'address' та 'port' у вашому файлі Jade над тегом сценарію, вони повинні відображатися.

Можливі рішення перераховані у питанні, до якого я пов’язаний вище, але ви можете: - передавати кожен рядок як нерозміщений текст (! = На початку кожного рядка), а просто ставити "-" перед кожним рядком javascript, що використовує a локальна змінна, або: - передайте змінні через елемент dom та доступ через JQuery (некрасиво)

Немає кращого способу? Здається, що творці Jade не хочуть підтримувати багатолінійний javascript, як це показує ця тема в GitHub: https://github.com/visionmedia/jade/pull/405

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.