¿Cómo puedo pasar datos de Flask a JavaScript en una plantilla?


123

Mi aplicación realiza una llamada a una API que devuelve un diccionario. Quiero pasar información de este dict a JavaScript en la vista. Estoy usando la API de Google Maps en el JS, específicamente, así que me gustaría pasarle una lista de tuplas con la información larga / lat. Sé que render_templatepasará estas variables a la vista para que puedan usarse en HTML, pero ¿cómo podría pasarlas a JavaScript en la plantilla?

from flask import Flask
from flask import render_template

app = Flask(__name__)

import foo_api

api = foo_api.API('API KEY')

@app.route('/')
def get_data():
    events = api.call(get_event, arg0, arg1)
    geocode = event['latitude'], event['longitude']
    return render_template('get_data.html', geocode=geocode)

Respuestas:


140

Puede usar {{ variable }}cualquier parte de su plantilla, no solo en la parte HTML. Entonces esto debería funcionar:

<html>
<head>
  <script>
    var someJavaScriptVar = '{{ geocode[1] }}';
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: {{ geocode[0] }} ' + someJavaScriptVar)" />
</body>
</html>

Piense en ello como un proceso de dos etapas: primero, Jinja (el motor de plantillas que utiliza Flask) genera su salida de texto. Esto se envía al usuario que ejecuta el JavaScript que ve. Si desea que su variable Flask esté disponible en JavaScript como una matriz, debe generar una definición de matriz en su salida:

<html>
  <head>
    <script>
      var myGeocode = ['{{ geocode[0] }}', '{{ geocode[1] }}'];
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Jinja también ofrece construcciones más avanzadas de Python, por lo que puede acortarlo a:

<html>
<head>
  <script>
    var myGeocode = [{{ ', '.join(geocode) }}];
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
</body>
</html>

También puede usar forbucles, ifdeclaraciones y muchos más, consulte la documentación de Jinja2 para obtener más información.

Además, eche un vistazo a la respuesta de Ford que señala el tojsonfiltro, que es una adición al conjunto estándar de filtros de Jinja2 .

Editar noviembre de 2018: tojsonahora está incluido en el conjunto estándar de filtros de Jinja2.


2
Muy agradecido, mensi! Ese fue mi pensamiento inicial, pero la documentación de Flask no deja en claro que puede usar el formulario {{var}} también en JS. Gracias por aclarar eso.
mea

2
@mea: también puede usar el motor de plantillas para generar archivos arbitrarios basados ​​en texto, también lo he usado para generar dinámicamente archivos TeX (-> PDF) y correo electrónico, es bastante versátil;)
mensi

pregunta de seguimiento rápida: si hago un bucle for en JS, ¿puedo usar la variable de índice dentro de la variable python, por ejemplo, {{geocode [i]}}?
mea

1
Eso tiene sentido, pero la solución que publicaste parece requerir que codifique manualmente el contenido de la matriz JS. Esperaba poder hacerlo de manera más programática para poder pasar una lista de Python de longitud variable y un bucle JS for podría iterar sobre la longitud y luego agregarlos a la matriz JS. Lo siento si no me estoy aclarando lo suficiente, pero soy bastante verde para JS y el desarrollador web.
mea

1
@RocketPingu si desea pasar datos en un archivo separado, generalmente es más fácil usar el jsonmódulo para volcar sus datos en forma de un objeto json
mensi

113

La forma ideal de lograr que cualquier objeto de Python se convierta en un objeto JavaScript es usar JSON. JSON es excelente como formato para transferir entre sistemas, pero a veces olvidamos que significa JavaScript Object Notation. Esto significa que inyectar JSON en la plantilla es lo mismo que inyectar código JavaScript que describe el objeto.

Flask proporciona un filtro Jinja para esto: tojsonvolca la estructura en una cadena JSON y la marca como segura para que Jinja no se escape automáticamente.

<html>
  <head>
    <script>
      var myGeocode = {{ geocode|tojson }};
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Esto funciona para cualquier estructura de Python que sea serializable JSON:

python_data = {
    'some_list': [4, 5, 6],
    'nested_dict': {'foo': 7, 'bar': 'a string'}
}
var data = {{ python_data|tojson }};
alert('Data: ' + data.some_list[1] + ' ' + data.nested_dict.foo + 
      ' ' + data.nested_dict.bar);

44
Intente esto la próxima vez que acceda Uncaught SyntaxError: Unexpected token &a la consola de JavaScript.
scharfmn

8
Encuentro esta respuesta más sólida y lógica que la respuesta aceptada.
Konrad

Recibí un error al ejecutar el código:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

El uso de un atributo de datos en un elemento HTML evita tener que usar secuencias de comandos en línea, lo que a su vez significa que puede usar reglas CSP más estrictas para una mayor seguridad.

Especifique un atributo de datos como este:

<div id="mydiv" data-geocode='{{ geocode|tojson }}'>...</div>

Luego acceda a él en un archivo JavaScript estático como este:

// Raw JavaScript
var geocode = JSON.parse(document.getElementById("mydiv").dataset.geocode);

// jQuery
var geocode = JSON.parse($("#mydiv").data("geocode"));

11

Alternativamente, podría agregar un punto final para devolver su variable:

@app.route("/api/geocode")
def geo_code():
    return jsonify(geocode)

Luego haz un XHR para recuperarlo:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })

Sí, podría hacerlo, y en algunas arquitecturas (especialmente en SPA) esta es la forma correcta de hacer las cosas, pero tenga en cuenta que existen varias desventajas de hacerlo en lugar de incluir los datos en la página cuando los sirve: 1. es más lento, 2. requiere un poco más de código incluso para hacerlo de manera descuidada, y 3. introduce al menos dos estados frontales adicionales que probablemente necesitará manejar limpiamente en su UI (es decir, el estado donde la solicitud XHR aún está en curso) , y en el que falló por completo), lo que requiere un montón de código JavaScript adicional e introduce una fuente adicional de posibles errores.
Mark Amery

3

Solo otra solución alternativa para aquellos que desean pasar variables a un script que se obtiene usando un matraz, solo logré hacer que esto funcione definiendo las variables fuera y luego llamando al script de la siguiente manera:

    <script>
    var myfileuri = "/static/my_csv.csv"
    var mytableid = 'mytable';
    </script>
    <script type="text/javascript" src="/static/test123.js"></script>

Si ingreso variables jinja en test123.jsél no funciona y obtendrá un error.


Exactamente lo que estaba buscando.
shrishinde

1
-1; Esta respuesta no tiene sentido. Creo que (basado en el enunciado "un script que se haya originado el uso de frasco" y su aparente expectativa de que usted sería capaz de utilizar variables de plantilla en /static/test123.js) que eres malentendido cómo <script>s con srcs trabajo. No son una característica de matraz. El navegador , cuando analiza dicho script, realiza una solicitud HTTP por separado para obtener el script. El contenido del script no se integra en la plantilla por Flask; de hecho, es probable que Flask haya terminado de enviar el HTML con plantilla al navegador para cuando el navegador incluso solicite el script.
Mark Amery

3

Ya se dan respuestas de trabajo, pero quiero agregar una verificación que actúa como una prueba de fallas en caso de que la variable frasco no esté disponible. Cuando usas:

var myVariable = {{ flaskvar | tojson }};

Si hay un error que hace que la variable sea inexistente, los errores resultantes pueden producir resultados inesperados. Para evitar esto:

{% if flaskvar is defined and flaskvar %}
var myVariable = {{ flaskvar | tojson }};
{% endif %}

2
<script>
    const geocodeArr = JSON.parse('{{ geocode | tojson }}');
    console.log(geocodeArr);
</script>

Esto usa jinja2 para convertir la tupla de geocodificación en una cadena json, y luego el javascript lo JSON.parseconvierte en una matriz de javascript.


1
¿Por qué no solo serializas el json en python?
Mojimi

0

Bueno, tengo un método complicado para este trabajo. La idea es la siguiente:

Cree algunas etiquetas HTML invisibles como <label>, <p>, <input>etc. en el cuerpo HTML y cree un patrón en la identificación de la etiqueta, por ejemplo, use el índice de la lista en la identificación de la etiqueta y el valor de la lista en el nombre de la clase de etiqueta.

Aquí tengo dos listas maintenance_next [] y maintenance_block_time [] de la misma longitud. Quiero pasar los datos de estas dos listas a javascript usando el matraz. Así que tomo una etiqueta de etiqueta invisible y establezco su nombre de etiqueta como un patrón de índice de lista y establezco su nombre de clase como valor en el índice.

{% for i in range(maintenance_next|length): %}
<label id="maintenance_next_{{i}}" name="{{maintenance_next[i]}}" style="display: none;"></label>
<label id="maintenance_block_time_{{i}}" name="{{maintenance_block_time[i]}}" style="display: none;"></label>
{% endfor%}

Después de esto, recupero los datos en javascript usando alguna operación simple de javascript.

<script>
var total_len = {{ total_len }};

for (var i = 0; i < total_len; i++) {
    var tm1 = document.getElementById("maintenance_next_" + i).getAttribute("name");
    var tm2 = document.getElementById("maintenance_block_time_" + i).getAttribute("name");
    
    //Do what you need to do with tm1 and tm2.
    
    console.log(tm1);
    console.log(tm2);
}
</script>


-1

Algunos archivos js provienen de la web o la biblioteca, no los ha escrito usted mismo. El código que obtienen variable como este:

var queryString = document.location.search.substring(1);
var params = PDFViewerApplication.parseQueryString(queryString);
var file = 'file' in params ? params.file : DEFAULT_URL;

Este método hace que los archivos js no cambien (mantenga la independencia) y pase la variable correctamente!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.