Come posso passare dati da Flask a JavaScript in un modello?


124

La mia app effettua una chiamata a un'API che restituisce un dizionario. Voglio passare le informazioni da questo dict a JavaScript nella vista. Sto utilizzando l'API di Google Maps nel JS, in particolare, quindi mi piacerebbe passare un elenco di tuple con le informazioni long / lat. So che render_templatepasserà queste variabili alla vista in modo che possano essere utilizzate in HTML, ma come potrei passarle a JavaScript nel modello?

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)

Risposte:


141

Puoi usarlo {{ variable }}ovunque nel tuo modello, non solo nella parte HTML. Quindi questo dovrebbe funzionare:

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

Consideralo come un processo in due fasi: in primo luogo, Jinja (il motore di modelli utilizzato da Flask) genera l'output di testo. Questo viene inviato all'utente che esegue il JavaScript che vede. Se vuoi che la tua variabile Flask sia disponibile in JavaScript come array, devi generare una definizione di array nel tuo output:

<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 offre anche costrutti più avanzati di Python, quindi puoi accorciarlo 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>

Puoi anche usare forloop, ifistruzioni e molto altro, vedi la documentazione di Jinja2 per ulteriori informazioni.

Inoltre, dai un'occhiata alla risposta di Ford che indica il tojsonfiltro che è un'aggiunta al set di filtri standard di Jinja2 .

Modifica novembre 2018: tojsonora è incluso nel set di filtri standard di Jinja2.


2
Molto obbligato, mensi! Questo era il mio pensiero iniziale, ma la documentazione per Flask non rende immediatamente chiaro che puoi usare il modulo {{var}} anche in JS. Grazie per aver chiarito tutto.
mea

2
@mea: puoi anche usare il motore dei modelli per generare file arbitrari basati su testo, l'ho anche usato per generare dinamicamente file TeX (-> PDF) ed email, è abbastanza versatile;)
mensi

Domanda rapida di follow-up: se eseguo un ciclo for in JS, posso utilizzare la variabile index all'interno della variabile python, ad esempio {{geocode [i]}}?
mea

1
Ha senso, ma la soluzione che hai pubblicato sembra richiedermi di codificare manualmente il contenuto dell'array JS. Speravo di poterlo fare in modo più programmatico in modo da poter passare un elenco Python di lunghezza variabile e un ciclo JS for potrebbe iterare sulla lunghezza e quindi aggiungerli all'array JS. Scusa se non mi sto rendendo abbastanza chiaro, ma sono piuttosto verde per JS e web dev.
mea

1
@RocketPingu se vuoi passare i dati in un file separato, di solito è più semplice usare il jsonmodulo per scaricare i tuoi dati sotto forma di un oggetto json
mensi

114

Il modo ideale per ottenere praticamente qualsiasi oggetto Python in un oggetto JavaScript è usare JSON. JSON è ottimo come formato per il trasferimento tra sistemi, ma a volte ci dimentichiamo che sta per JavaScript Object Notation. Ciò significa che l'inserimento di JSON nel modello equivale a inserire il codice JavaScript che descrive l'oggetto.

Flask fornisce un filtro Jinja per questo: tojsonscarica la struttura su una stringa JSON e la contrassegna come sicura in modo che Jinja non la scappi automaticamente.

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

Funziona per qualsiasi struttura Python serializzabile in 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);

4
Prova questo la prossima volta che Uncaught SyntaxError: Unexpected token &accedi alla console javascript.
scharfmn

8
Trovo questa risposta più solida e logica della risposta accettata.
Konrad

Ho ricevuto un errore durante l'esecuzione del codice:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

L'utilizzo di un attributo di dati su un elemento HTML evita di dover utilizzare script in linea, il che a sua volta significa che è possibile utilizzare regole CSP più rigide per una maggiore sicurezza.

Specifica un attributo di dati in questo modo:

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

Quindi accedilo in un file JavaScript statico in questo modo:

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

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

11

In alternativa puoi aggiungere un endpoint per restituire la tua variabile:

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

Quindi esegui un XHR per recuperarlo:

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

Sì, è possibile , e in alcune architetture (esp ZPS). Questo è il modo corretto di fare le cose, ma tenere a mente che ci sono diversi svantaggi a fare questo contro la cottura i dati nella pagina al momento della pubblicazione: 1. è più lento, 2. richiede un po 'più di codice anche per essere eseguito in modo sciatto, e 3. introduce almeno due stati front-end aggiuntivi che probabilmente dovrai gestire in modo pulito nella tua interfaccia utente (vale a dire lo stato in cui la richiesta XHR è ancora in volo , e quello in cui è fallito completamente), che richiede un po 'di codice JavaScript aggiuntivo e introduce un'ulteriore fonte di potenziali bug.
Mark Amery

3

Solo un'altra soluzione alternativa per coloro che vogliono passare variabili a uno script che proviene da flask, sono riuscito a farlo funzionare solo definendo le variabili esterne e quindi chiamando lo script come segue:

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

Se inserisco variabili jinja test123.jsnon funziona e riceverai un errore.


Esattamente quello che stavo cercando.
shrishinde

1
-1; questa risposta non ha senso. Penso (sulla base della frase "uno script che proviene da flask" e dalla tua apparente aspettativa di essere in grado di utilizzare le variabili del modello in /static/test123.js) che tu stia fraintendendo come funziona <script>s with srcs. Non sono una funzionalità di Flask. Il browser , quando analizza uno script di questo tipo, effettua una richiesta HTTP separata per ottenere lo script. Il contenuto dello script non viene inserito nel modello da Flask; infatti, Flask ha probabilmente finito di inviare il modello HTML al browser prima che il browser richieda lo script.
Mark Amery

3

Le risposte di lavoro sono già fornite ma voglio aggiungere un controllo che funge da sicurezza nel caso in cui la variabile flask non sia disponibile. Quando usi:

var myVariable = {{ flaskvar | tojson }};

se c'è un errore che rende la variabile inesistente, gli errori risultanti possono produrre risultati imprevisti. Per evitare questo:

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

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

Questo utilizza jinja2 per trasformare la tupla del codice geografico in una stringa json, quindi javascript la JSON.parsetrasforma in un array javascript.


1
Perché non serializzi il json in python
Mojimi

0

Bene, ho un metodo complicato per questo lavoro. L'idea è la seguente

Crea alcuni tag HTML invisibili come <label>, <p>, <input>ecc. Nel corpo dell'HTML e crea un pattern in tag id, ad esempio, usa list index in tag id e list value in tag class name.

Qui ho due elenchi maintenance_next [] e maintenance_block_time [] della stessa lunghezza. Voglio passare i dati di questi due elenchi a JavaScript usando il pallone. Quindi prendo un tag di etichetta invisibile e imposto il suo nome di tag come un modello di indice di lista e imposto il suo nome di classe come valore in index.

{% 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%}

Dopodiché, recupero i dati in javascript utilizzando alcune semplici operazioni 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

Alcuni file js provengono dal Web o dalla libreria, non vengono scritti da te. Il codice che ottengono variabile in questo modo:

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

Questo metodo rende i file js invariati (mantiene l'indipendenza) e passa la variabile correttamente!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.