จะส่งตัวแปรจากไฟล์เทมเพลต Jade ไปยังไฟล์สคริปต์ได้อย่างไร


119

ฉันมีปัญหากับตัวแปร (config) ที่ประกาศในไฟล์แม่แบบ jade (index.jade) ที่ไม่ได้ส่งไปยังไฟล์จาวาสคริปต์ซึ่งทำให้จาวาสคริปต์ของฉันขัดข้อง นี่คือไฟล์ (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 (เครื่องของฉันเอง) ฉันใช้ node-inspector สำหรับการดีบักซึ่งบอกฉันว่าตัวแปร config ไม่ได้กำหนดไว้ใน public / javascripts / app.js

ความคิดใด ๆ ?? ขอบคุณ !!


คำตอบ:


174

มันเป็นเพียงเล็กน้อยปลาย แต่ ...

script.
  loginName="#{login}";

สิ่งนี้ทำงานได้ดีในสคริปต์ของฉัน ใน Express ฉันกำลังทำสิ่งนี้:

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

ฉันเดาว่าหยกล่าสุดแตกต่างกันอย่างไร?

Merc

แก้ไข: เพิ่ม "." หลังจากสคริปต์เพื่อป้องกันหยกเตือน


1
ขอบคุณที่ตอบรับคำตอบ! แล้วปัญหาที่แท้จริงของโค้ดของคุณคืออะไร?
Merc

ใช่ฉันทำให้สิ่งนี้ใช้ได้เช่นกันโดยการตัดตัวแปรท้องถิ่นในเทมเพลตในเครื่องหมายคำพูดและเครื่องหมาย # {}
Askdesigners

คำตอบนี้อันตรายคำตอบของ lagginreflex เข้ารหัสสตริงอย่างถูกต้อง (การขึ้นบรรทัดใหม่ในชื่อล็อกอินอาจทำให้สามารถเรียกใช้โค้ดได้)
Paul Grove

ดังนั้นปัญหาจึงเป็นเพียงเครื่องหมายคำพูดเดียวในขณะที่คาดว่าจะเป็นสองเท่าใช่ไหม
Augustin Riedinger

สำหรับฉันปัญหาคือจุด ฉันมีมันในตอนท้ายของบล็อกสคริปต์ ด้วยจุดหลังคีย์เวิร์ด "script" จะทำงานได้อย่างมีเสน่ห์ ขอบคุณ!
Mirko

103

!{}มีไว้สำหรับการแก้ไขโค้ดที่ไม่ใช้ Escapeซึ่งเหมาะสำหรับวัตถุมากกว่า

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


#{}ใช้สำหรับการแก้ไขสตริงที่ใช้ Escape ซึ่งเหมาะสำหรับกรณีที่คุณกำลังทำงานกับสตริงเท่านั้น มันไม่ทำงานกับวัตถุ

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

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>

1
ตอบโจทย์สุด ๆ ! ฉันค้นหาหัวข้อนี้มานานกว่า 20 นาทีแล้วและไม่มีคำตอบอื่นใดที่ระบุความแตกต่างระหว่าง! {} และ # {} ฉันแค่คิดตลอดเวลานี้! {] เทียบเท่ากับ # {} ที่เลิกใช้แล้ว ... ขอบคุณอีกครั้ง
Patrick E.

ไปด้านบน! คำตอบที่ดี
Scarysize

คำตอบที่ดีและใช้ได้กับวัตถุ! Btw เป็นJSONวัตถุระดับโลกหรือไม่? ดูเหมือนว่าจะใช้งานได้โดยไม่ต้องนำเข้าไลบรารีอื่น ๆ ถ้าเป็นเช่นนั้นการรองรับเบราว์เซอร์ล่ะ
chharvey

@chharvey JSONเป็นภาษาจาวาสคริปต์ในตัว (เช่น Date หรือ Math) เบราว์เซอร์ทั้งหมดรองรับ
laggingreflex

1
เห็นด้วยว่านี่เป็นคำตอบที่ดีที่สุด แต่ฉันไม่คิดว่านี่เป็นสถานที่ที่เหมาะสมในการหลบหนีจากสิ่งที่อาจเป็นอันตราย IMO จะเป็นการดีกว่าที่จะละเว้นการreplaceเรียกในรหัสนี้และปฏิบัติต่อค่านี้เช่นเดียวกับอินพุตอื่น ๆ JSON.stringify ได้รับการรับประกันว่าจะสร้าง javascript ที่ถูกต้อง (หรือล้มเหลว) และหลังจากที่คุณมีค่าที่อาจเป็นอันตรายนี้ในหน่วยความจำคุณสามารถตรวจสอบให้แน่ใจว่าได้ฆ่าเชื้อตามความจำเป็นก่อนใช้งาน
Riley Lark

9

ในกรณีของฉันฉันกำลังพยายามส่งผ่านวัตถุไปยังเทมเพลตผ่านเส้นทางด่วน (คล้ายกับการตั้งค่า OPs) จากนั้นฉันต้องการส่งผ่านวัตถุนั้นไปยังฟังก์ชันที่ฉันเรียกผ่านแท็กสคริปต์ในเทมเพลตปั๊ก แม้ว่าคำตอบของ lagginreflex จะทำให้ฉันเข้าใกล้ แต่ฉันก็จบลงด้วยสิ่งต่อไปนี้:

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

สิ่งนี้ทำให้มั่นใจได้ว่าออบเจ็กต์ถูกส่งผ่านไปตามที่คาดไว้แทนที่จะต้อง deserialise ในฟังก์ชัน นอกจากนี้คำตอบอื่น ๆ ดูเหมือนจะทำงานได้ดีกับ primitives แต่เมื่ออาร์เรย์ ฯลฯ ถูกส่งผ่านไปพร้อมกับวัตถุพวกเขาจะถูกแยกวิเคราะห์เป็นค่าสตริง


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วัตถุจริงของเบราว์เซอร์ของคุณซึ่งคุณสามารถใช้ใน Reactjs, Angular, ... หรือวานิลลาจาวาสคริปต์


1
นี่คือคำตอบที่ดีที่สุดที่ฉันได้เห็น
Tohir

3

นี่คือวิธีที่ฉันพูดถึงเรื่องนี้ (โดยใช้อนุพันธ์ MEAN)

ตัวแปรของฉัน:

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

ก่อนอื่นฉันต้องตรวจสอบให้แน่ใจว่ามีการส่งผ่านตัวแปร config ที่จำเป็น MEAN ใช้แพ็คเกจโหนด 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

หมายเหตุ: ฉันรีเซ็ต "heirarchy indicator" เป็น "__" (ขีดล่างสองเท่า) เนื่องจากค่าเริ่มต้นคือ ":" ซึ่งทำให้ตัวแปรตั้งค่าจาก bash ได้ยากขึ้น ดูโพสต์อื่นในกระทู้นี้

ตอนนี้ส่วนของหยก: ถัดไปจำเป็นต้องแสดงค่าเพื่อให้จาวาสคริปต์สามารถเลือกได้จากฝั่งไคลเอ็นต์ วิธีง่ายๆในการเขียนค่าเหล่านี้ลงในไฟล์ดัชนี เนื่องจากเป็นแอปหน้าเดียว (เชิงมุม) หน้านี้จึงโหลดก่อนเสมอ ฉันคิดว่านี่ควรเป็นไฟล์รวมจาวาสคริปต์ (เพื่อให้ทุกอย่างสะอาด) แต่นี่เป็นสิ่งที่ดีสำหรับการสาธิต

app / ควบคุม / 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 และอื่น ๆ นั้นไม่เกี่ยวข้อง คำถามคือเกี่ยวกับวิธีการส่งผ่านตัวแปรไปยังไคลเอนต์ไม่ใช่เกี่ยวกับวิธีที่คุณทำ dev stack ของคุณ แม้ว่าจะไม่ลงคะแนนเนื่องจากคุณยังคงใช้ความพยายามอย่างมากในคำตอบนี้
Mrchief

ซับซ้อนเกินไป
andygoestohollywood

2

ดูคำถามนี้: JADE + EXPRESS: การวนซ้ำวัตถุในโค้ด JS แบบอินไลน์ (ฝั่งไคลเอ็นต์)?

ฉันมีปัญหาเดียวกัน Jade ไม่ส่งผ่านตัวแปรท้องถิ่นใน (หรือทำเทมเพลตใด ๆ เลย) ไปยังสคริปต์จาวาสคริปต์เพียงแค่ส่งผ่านบล็อกทั้งหมดในรูปแบบข้อความตามตัวอักษร หากคุณใช้ตัวแปรโลคัล 'address' และ 'port' ในไฟล์ Jade ของคุณเหนือแท็กสคริปต์ก็จะปรากฏขึ้น

วิธีแก้ปัญหาที่เป็นไปได้แสดงอยู่ในคำถามที่ฉันเชื่อมโยงไว้ด้านบน แต่คุณสามารถ: - ส่งทุกบรรทัดเป็นข้อความที่ไม่ใช้ Escape (! = ที่จุดเริ่มต้นของทุกบรรทัด) และใส่ "-" ก่อนทุกบรรทัดของจาวาสคริปต์ที่ใช้ a ตัวแปรท้องถิ่นหรือ: - ส่งผ่านตัวแปรผ่านองค์ประกอบ dom และเข้าถึงผ่าน JQuery (น่าเกลียด)

ไม่มีวิธีที่ดีกว่านี้หรือ ดูเหมือนว่าผู้สร้าง Jade ไม่ต้องการการสนับสนุนจาวาสคริปต์แบบหลายบรรทัดดังที่แสดงในเธรดนี้ใน GitHub: https://github.com/visionmedia/jade/pull/405

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.