Wie kann ich Daten von Flask in einer Vorlage an JavaScript übergeben?

123

Meine App ruft eine API auf, die ein Wörterbuch zurückgibt. Ich möchte Informationen aus diesem Diktat in der Ansicht an JavaScript übergeben. Ich verwende die Google Maps-API speziell in JS, daher möchte ich ihr eine Liste mit Tupeln mit den Long / Lat-Informationen übergeben. Ich weiß, dass render_templatediese Variablen an die Ansicht übergeben werden, damit sie in HTML verwendet werden können. Wie kann ich sie jedoch in der Vorlage an JavaScript übergeben?

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)
mea
quelle

Antworten:

140

Sie können {{ variable }}überall in Ihrer Vorlage verwenden, nicht nur im HTML-Teil. Das sollte also funktionieren:

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

Stellen Sie sich das als zweistufigen Prozess vor: Zunächst generiert Jinja (die von Flask verwendete Template-Engine) Ihre Textausgabe. Dies wird an den Benutzer gesendet, der das angezeigte JavaScript ausführt. Wenn Ihre Flask-Variable in JavaScript als Array verfügbar sein soll, müssen Sie in Ihrer Ausgabe eine Array-Definition generieren:

<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 bietet auch erweiterte Konstrukte aus Python an, sodass Sie es verkürzen können auf:

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

Sie können auch forSchleifen, ifAnweisungen und vieles mehr verwenden. Weitere Informationen finden Sie in der Jinja2-Dokumentation .

Schauen Sie sich auch Fords Antwort an, die auf den tojsonFilter hinweist, der eine Ergänzung zu Jinja2s Standardfiltersatz darstellt .

Edit Nov 2018: tojsonist jetzt im Standard-Filtersatz von Jinja2 enthalten.

mensi
quelle
2
Sehr verpflichtet, mensi! Das war mein erster Gedanke, aber die Dokumentation für Flask macht nicht ohne weiteres klar, dass Sie das Formular {{var}} auch in JS verwenden können. Danke, dass du das geklärt hast.
Mea
2
@mea: Sie können auch die Template-Engine verwenden, um beliebige textbasierte Dateien zu generieren. Ich habe sie auch verwendet, um TeX-Dateien (-> PDF) und E-Mail dynamisch zu generieren. Es ist sehr vielseitig.)
mensi
Schnelle Folgefrage: Wenn ich eine for-Schleife in JS mache, kann ich die Indexvariable innerhalb der Python-Variablen verwenden, z. B. {{geocode [i]}}?
Mea
1
Das ist sinnvoll, aber für die von Ihnen veröffentlichte Lösung muss ich den Inhalt des JS-Arrays manuell codieren. Ich hatte gehofft, ich könnte es programmatischer machen, so dass ich eine Python-Liste mit variabler Länge übergeben und eine JS for-Schleife über die Länge iterieren und sie dann an das JS-Array anhängen könnte. Es tut mir leid, wenn ich mich nicht klar genug mache, aber ich bin JS und Webentwicklern gegenüber eher grün.
Mea
1
@ RocketPingu Wenn Sie Daten in einer separaten Datei übergeben möchten, ist es normalerweise einfacher, das jsonModul zu verwenden, um Ihre Daten in Form eines
JSON-
113

Der ideale Weg, um so ziemlich jedes Python-Objekt in ein JavaScript-Objekt zu integrieren, ist die Verwendung von JSON. JSON eignet sich hervorragend als Format für die Übertragung zwischen Systemen, aber manchmal vergessen wir, dass es für JavaScript Object Notation steht. Dies bedeutet, dass das Einfügen von JSON in die Vorlage dem Einfügen von JavaScript-Code entspricht, der das Objekt beschreibt.

Flask bietet hierfür einen Jinja-Filter: tojsonspeichert die Struktur in einer JSON-Zeichenfolge und markiert sie als sicher, damit Jinja sie nicht automatisch erkennt.

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

Dies funktioniert für jede Python-Struktur, die JSON-serialisierbar ist:

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);
Ford
quelle
4
Versuchen Sie dies, wenn Sie das nächste Mal Uncaught SyntaxError: Unexpected token &in die Javascript-Konsole gelangen.
scharfmn
8
Ich finde diese Antwort solider und logischer als die akzeptierte Antwort.
Konrad
Beim Ausführen des Codes ist ein Fehler TypeError: Object of type Undefined is not JSON serializable
aufgetreten
27

Durch die Verwendung eines Datenattributs für ein HTML-Element wird die Verwendung von Inline-Skripten vermieden, was wiederum bedeutet, dass Sie strengere CSP-Regeln verwenden können, um die Sicherheit zu erhöhen.

Geben Sie ein Datenattribut wie folgt an:

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

Greifen Sie dann in einer statischen JavaScript-Datei wie folgt darauf zu:

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

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

Alternativ können Sie einen Endpunkt hinzufügen, um Ihre Variable zurückzugeben:

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

Führen Sie dann eine XHR durch, um sie abzurufen:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })
Vinnie James
quelle
Ja, das könnten Sie , und in einigen Architekturen (insbesondere SPAs) ist dies die richtige Vorgehensweise. Beachten Sie jedoch , dass dies mehrere Nachteile hat, wenn Sie die Daten in die Seite einbinden, wenn Sie sie bereitstellen: 1. Es ist langsamer, 2. es erfordert etwas mehr Code, um schlampig zu arbeiten, und 3. es führt mindestens zwei zusätzliche Front-End-Zustände ein, die Sie wahrscheinlich sauber in Ihrer Benutzeroberfläche behandeln müssen (nämlich den Zustand, in dem die XHR-Anforderung noch im Flug ist und derjenige, bei dem es vollständig fehlgeschlagen ist), der eine Menge zusätzlichen JavaScript-Codes erfordert und eine zusätzliche Quelle potenzieller Fehler einführt.
Mark Amery
3

Nur eine weitere alternative Lösung für diejenigen, die Variablen an ein Skript übergeben möchten, das mit flask bezogen wurde. Ich habe es nur geschafft, dies zum Laufen zu bringen, indem ich die Variablen außerhalb definiert und das Skript dann wie folgt aufgerufen habe:

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

Wenn ich Jinja-Variablen eingebe, test123.jsfunktioniert das nicht und Sie erhalten eine Fehlermeldung.

Tsando
quelle
Genau das, wonach ich gesucht habe.
Shrishinde
1
-1; Diese Antwort macht keinen Sinn. Ich denke (basierend auf der Formulierung "ein Skript, das mit flask bezogen wird" und Ihrer offensichtlichen Erwartung, dass Sie Vorlagenvariablen verwenden können /static/test123.js), dass Sie falsch verstehen, wie es <script>mit srcs funktioniert. Sie sind keine Flask-Funktion. Der Browser , wenn es ein solches Skript parst, macht eine separate HTTP - Anforderung das Skript zu erhalten. Der Skriptinhalt wird von Flask nicht in die Vorlage eingebrannt. In der Tat hat Flask das Senden des HTML-Vorlagen an den Browser wahrscheinlich abgeschlossen, als der Browser das Skript überhaupt anfordert.
Mark Amery
3

Arbeitsantworten sind bereits gegeben, aber ich möchte eine Prüfung hinzufügen, die als ausfallsicher fungiert, falls die Kolbenvariable nicht verfügbar ist. Wenn Sie verwenden:

var myVariable = {{ flaskvar | tojson }};

Wenn ein Fehler vorliegt, der dazu führt, dass die Variable nicht vorhanden ist, können die daraus resultierenden Fehler zu unerwarteten Ergebnissen führen. Um es zu umgehen:

{% if flaskvar is defined and flaskvar %}
var myVariable = {{ flaskvar | tojson }};
{% endif %}
Patrick Mutuku
quelle
2
<script>
    const geocodeArr = JSON.parse('{{ geocode | tojson }}');
    console.log(geocodeArr);
</script>

Dies verwendet jinja2, um das Geocode-Tupel in einen json-String umzuwandeln, und dann JSON.parseverwandelt das Javascript das in ein Javascript-Array.

nackjicholson
quelle
1
Warum serialisieren Sie nicht einfach den JSON in Python
Mojimi
0

Nun, ich habe eine schwierige Methode für diesen Job. Die Idee ist wie folgt:

Erstellen Sie einige unsichtbare HTML-Tags wie <label>, <p>, <input>usw. im HTML-Text und erstellen Sie ein Muster in der Tag-ID. Verwenden Sie beispielsweise den Listenindex in der Tag-ID und den Listenwert im Namen der Tag-Klasse.

Hier habe ich zwei Listen wartungs_next [] und wartungsblockzeit [] gleicher Länge. Ich möchte die Daten dieser beiden Listen mit der Flasche an Javascript übergeben. Also nehme ich ein unsichtbares Label-Tag und setze seinen Tag-Namen als Muster des Listenindex und setze seinen Klassennamen als Wert am 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%}

Danach rufe ich die Daten in Javascript mit einer einfachen Javascript-Operation ab.

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

Tanmoy Datta
quelle
-1

Einige js-Dateien stammen aus dem Internet oder der Bibliothek und werden nicht von Ihnen selbst geschrieben. Der Code, den sie erhalten, ist wie folgt variabel:

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

Diese Methode macht js-Dateien unverändert (Unabhängigkeit bewahren) und übergeben Variablen korrekt!

jayzhen
quelle