Como passar a variável do arquivo de modelo jade para um arquivo de script?

119

Estou tendo problemas com uma variável (config) declarada em um arquivo de modelo jade (index.jade) que não é passado para um arquivo javascript, o que faz meu javascript travar. Aqui está o arquivo (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')

Aqui está uma parte do meu app.js (lado do servidor):

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

E aqui está uma parte do meu arquivo javascript que trava (public / javascripts / app.js):

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

Estou executando o site em modo de desenvolvimento (NODE_ENV = desenvolvimento) no localhost (minha própria máquina). Estou usando o node-inspector para depuração, que me disse que a variável de configuração é indefinida em public / javascripts / app.js.

Alguma ideia?? Obrigado!!

Michael Eilers Smith
fonte
possível duplicata de variáveis ​​ExpressJS Pass para JavaScript
laggingreflex

Respostas:

174

É um pouco tarde, mas ...

script.
  loginName="#{login}";

Isso está funcionando bem no meu script. No Express, estou fazendo o seguinte:

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

Eu acho que o jade mais recente é diferente?

Merc.

editar: adicionado "." após o script para evitar o aviso Jade.

Merc
fonte
1
Obrigado por aceitar a resposta! Então, qual era o problema real com seu código?
Merc
Sim, fiz isso funcionar também envolvendo a variável local no modelo entre aspas e o indicador # {}.
Askdesigners
Esta resposta é perigosa, a resposta de lagginreflex codifica corretamente a string (novas linhas no nome de login podem permitir a execução do código).
Paul Grove
Portanto, o problema era apenas as aspas simples, enquanto se espera o dobro, certo?
Augustin Riedinger,
Para mim, o problema era o ponto. Eu o tinha no final do bloco de script. Com o ponto após a palavra-chave "script" funciona perfeitamente. Obrigado!
Mirko
102

!{}é para interpolação de código sem escape , que é mais adequado para objetos

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>

A ideia é evitar que o invasor:

  1. Romper com a variável: JSON.stringifyescapa das aspas
  2. Saia da tag do script: se o conteúdo da variável (que você pode não ser capaz de controlar se vier do banco de dados, por exemplo) tiver uma </script>string, a instrução replace cuidará disso

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


#{}é para interpolação de string com escape , o que é adequado apenas se você estiver trabalhando com strings. Não funciona com objetos

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

//=> <script>var data = {&quot;foo&quot;:&quot;bar&quot;}</script>
laggingreflex
fonte
1
Excelente resposta! Estou pesquisando este assunto há mais de 20 minutos e nenhuma outra resposta indicou a diferença entre! {} E # {}. Todo esse tempo eu pensei! {] Era o equivalente obsoleto de # {} ... Obrigado novamente.
Patrick E.
Para o topo! Ótima resposta.
Scarysize
Ótima resposta, e funciona para objetos! Aliás, é JSONum objeto global? Pareceu funcionar sem importar nenhuma outra biblioteca. Em caso afirmativo, e quanto ao suporte do navegador?
chharvey
@chharvey JSON é uma linguagem javascript embutida (como Date ou Math), todos os navegadores a suportam.
laggingreflex
1
Concordo que essa é a melhor resposta, mas não acho que este seja o lugar certo para escapar de cordas potencialmente perigosas. IMO, seria melhor omitir a replacechamada neste código e tratar este valor como qualquer outra entrada. JSON.stringify tem garantia de produzir javascript válido (ou falhar) e, depois de ter esse valor potencialmente perigoso na memória, certifique-se de limpá-lo conforme necessário antes de usar.
Riley Lark
9

No meu caso, estava tentando passar um objeto para um modelo por meio de uma rota expressa (semelhante à configuração de OPs). Então eu queria passar esse objeto para uma função que estava chamando por meio de uma tag de script em um modelo de pug. Embora a resposta de lagginreflex tenha me aproximado, acabei com o seguinte:

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

Isso garantiu que o objeto fosse passado conforme o esperado, em vez de precisar ser desserializado na função. Além disso, as outras respostas pareciam funcionar bem com primitivas, mas quando arrays etc. foram passados ​​junto com o objeto, eles foram analisados ​​como valores de string.

Kyle G
fonte
7

Se você é como eu e usa muito esse método de passar variáveis, aqui está uma solução de código sem gravação.

Em sua rota node.js, passe as variáveis ​​em um objeto chamado window, assim:

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

Então, em seu arquivo de layout pug / jade (logo antes de block content), você os obtém assim:

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

Conforme meu arquivo layout.pug é carregado com cada arquivo pug, não preciso 'importar' minhas variáveis ​​repetidamente.

Desta forma, todas as variáveis ​​/ objetos passados ​​para window'magicamente' acabam no windowobjeto real do seu navegador, onde você pode usá-los em Reactjs, Angular, ... ou vanilla javascript.

Sam
fonte
1
Esta é a melhor resposta que já vi.
Tohir
3

Veja como resolvi isso (usando um derivado MEAN)

Minhas variáveis:

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

Primeiro eu tive que ter certeza de que as variáveis ​​de configuração necessárias estavam sendo passadas. MEAN usa o pacote node nconf e, por padrão, é configurado para limitar quais variáveis ​​são passadas do ambiente. Eu tive que remediar isso:

config / config.js:

original:

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

após as modificações:

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

Agora posso definir minhas variáveis ​​assim:

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

Nota: Eu redefini o "indicador de hierarquia" para "__" (sublinhado duplo) porque seu padrão era ":", o que torna as variáveis ​​mais difíceis de definir no bash. Veja outra postagem neste tópico.

Agora a parte jade: em seguida, os valores precisam ser renderizados, para que o javascript possa captá-los no lado do cliente. Uma maneira direta de gravar esses valores no arquivo de índice. Como este é um aplicativo de uma página (angular), esta página é sempre carregada primeiro. Acho que o ideal é que este seja um arquivo de inclusão javascript (apenas para manter as coisas limpas), mas isso é bom para uma demonstração.

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

No meu html renderizado, isso me dá um pequeno script agradável:

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

A partir deste ponto, é fácil para o Angular utilizar o código.

Michael
fonte
Esta é uma maneira muito prolixa de dizer o que a resposta aceita já diz. Além disso, mencionar MEAN, nconf etc. é irrelevante. A questão é sobre como passar variáveis ​​para o cliente, não sobre como você fez sua pilha de dev. No entanto, não downvoting, já que você ainda se esforçou muito para responder.
Mrchief de
excessivamente complicado
andygoestohollywood
2

Veja esta pergunta: JADE + EXPRESS: Iterando sobre o objeto no código JS embutido (lado do cliente)?

Estou tendo o mesmo problema. Jade não passa variáveis ​​locais (ou faz qualquer modelo) para scripts javascript, ele simplesmente passa o bloco inteiro como texto literal. Se você usar as variáveis ​​locais 'endereço' e 'porta' em seu arquivo Jade acima da tag do script, elas deverão aparecer.

As soluções possíveis estão listadas na pergunta que indiquei acima, mas você pode: - passar cada linha como texto sem escape (! = No início de cada linha) e simplesmente colocar "-" antes de cada linha de javascript que usa um variável local, ou: - Passe variáveis ​​por meio de um elemento dom e acesse por meio de JQuery (feio)

Não há maneira melhor? Parece que os criadores do Jade não querem suporte a javascript de várias linhas, como mostrado por este tópico no GitHub: https://github.com/visionmedia/jade/pull/405

Varun Singh
fonte