Bagaimana cara mengirimkan data dari Flask ke JavaScript dalam template?


124

Aplikasi saya melakukan panggilan ke API yang mengembalikan kamus. Saya ingin meneruskan informasi dari dikt ini ke JavaScript dalam tampilan. Saya menggunakan Google Maps API di JS, khususnya, jadi saya ingin memberikan daftar tupel dengan informasi long / lat. Saya tahu itu render_templateakan meneruskan variabel ini ke tampilan sehingga bisa digunakan dalam HTML, tapi bagaimana saya bisa meneruskannya ke JavaScript di template?

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)

Jawaban:


141

Anda dapat menggunakan {{ variable }}di mana saja di template Anda, tidak hanya di bagian HTML. Jadi ini seharusnya berhasil:

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

Anggap saja sebagai proses dua tahap: Pertama, Jinja (mesin template yang digunakan Flask) menghasilkan keluaran teks Anda. Ini akan dikirim ke pengguna yang menjalankan JavaScript yang dia lihat. Jika Anda ingin variabel Flask Anda tersedia di JavaScript sebagai sebuah array, Anda harus membuat definisi array di output Anda:

<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 juga menawarkan konstruksi yang lebih canggih dari Python, jadi Anda dapat mempersingkatnya menjadi:

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

Anda juga dapat menggunakan forloop, ifpernyataan , dan banyak lagi, lihat dokumentasi Jinja2 untuk selengkapnya.

Selain itu, lihat jawaban Ford yang menunjukkan tojsonfilter yang merupakan tambahan dari kumpulan filter standar Jinja2 .

Sunting November 2018: tojsonsekarang termasuk dalam kumpulan filter standar Jinja2.


2
Terima kasih banyak, mensi! Itu adalah pemikiran awal saya, tetapi dokumentasi untuk Flask tidak menjelaskan dengan jelas bahwa Anda juga dapat menggunakan formulir {{var}} di JS. Terima kasih sudah membereskannya.
mea

2
@mea: Anda juga dapat menggunakan mesin templat untuk menghasilkan file berbasis teks arbitrer, saya juga telah menggunakannya untuk secara dinamis menghasilkan file TeX (-> PDF) dan email, ini cukup serbaguna;)
mensi

pertanyaan tindak lanjut cepat: jika saya melakukan perulangan for di JS, dapatkah saya menggunakan variabel indeks dalam variabel python misalnya {{geocode [i]}}?
mea

1
Itu masuk akal, tetapi solusi yang Anda posting tampaknya mengharuskan saya untuk membuat kode tangan dalam konten larik JS. Saya berharap saya bisa melakukannya secara lebih terprogram sehingga saya bisa melewati daftar panjang variabel Python dan JS for loop dapat mengulang panjang dan kemudian menambahkannya ke array JS. Maaf jika saya tidak membuat diri saya cukup jelas, tetapi saya agak hijau terhadap JS dan web dev.
mea

1
@RocketPingu jika Anda ingin meneruskan data dalam file terpisah, biasanya lebih mudah menggunakan jsonmodul untuk membuang data Anda dalam bentuk objek json
mensi

114

Cara ideal untuk memasukkan hampir semua objek Python ke dalam objek JavaScript adalah dengan menggunakan JSON. JSON sangat bagus sebagai format untuk transfer antar sistem, tetapi terkadang kita lupa bahwa itu adalah singkatan dari JavaScript Object Notation. Artinya memasukkan JSON ke dalam template sama dengan memasukkan kode JavaScript yang mendeskripsikan objek tersebut.

Flask menyediakan filter Jinja untuk ini: tojsonmembuang struktur ke string JSON dan menandainya aman sehingga Jinja tidak melakukan autoescape.

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

Ini berfungsi untuk semua struktur Python yang dapat diserialkan 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
Coba ini saat berikutnya Anda masuk Uncaught SyntaxError: Unexpected token &ke konsol javascript.
scharfmn

8
Saya menemukan jawaban ini lebih solid dan logis daripada jawaban yang diterima.
Konrad

Saya mendapat kesalahan menjalankan kode:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

Menggunakan atribut data pada elemen HTML menghindari penggunaan skrip inline, yang berarti Anda dapat menggunakan aturan CSP yang lebih ketat untuk meningkatkan keamanan.

Tentukan atribut data seperti ini:

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

Kemudian akses dalam file JavaScript statis seperti:

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

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

11

Atau Anda dapat menambahkan titik akhir untuk mengembalikan variabel Anda:

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

Kemudian lakukan XHR untuk mengambilnya:

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

Ya, Anda bisa , dan dalam beberapa arsitektur (khususnya SPA) ini adalah cara yang tepat untuk melakukan sesuatu, namun perlu diingat bahwa ada beberapa kelemahan melakukan hal ini dibandingkan memanggang data ke dalam halaman saat Anda menyajikannya: 1. itu lebih lambat, 2. memerlukan sedikit lebih banyak kode bahkan untuk dilakukan dengan sembrono, dan 3. ini memperkenalkan setidaknya dua status front-end ekstra yang mungkin perlu Anda tangani dengan rapi di UI Anda (yaitu status tempat permintaan XHR masih berjalan , dan yang gagal sepenuhnya), yang membutuhkan banyak kode JavaScript tambahan dan memperkenalkan sumber tambahan potensi bug.
Mark Amery

3

Hanya solusi alternatif lain bagi mereka yang ingin meneruskan variabel ke skrip yang bersumber menggunakan flask, saya hanya berhasil membuat ini berfungsi dengan mendefinisikan variabel di luar dan kemudian memanggil skrip sebagai berikut:

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

Jika saya memasukkan variabel jinja di test123.jsdalamnya tidak berfungsi dan Anda akan mendapatkan kesalahan.


Persis apa yang saya cari.
shrishinde

1
-1; jawaban ini tidak masuk akal. Saya pikir (berdasarkan frase "skrip yang bersumber menggunakan flask" dan harapan Anda yang jelas bahwa Anda akan dapat menggunakan variabel template di /static/test123.js) bahwa Anda salah paham bagaimana <script>s dengan srcs bekerja. Itu bukan fitur Flask. The Browser , ketika mem-parsing seperti script, membuat permintaan HTTP terpisah untuk mendapatkan script. Konten skrip tidak dimasukkan ke dalam template oleh Flask; memang, Flask sepertinya telah selesai mengirimkan HTML template ke browser pada saat browser bahkan meminta skrip.
Mark Amery

3

Jawaban yang berfungsi sudah diberikan, tetapi saya ingin menambahkan tanda centang yang berfungsi sebagai fail-safe jika variabel flask tidak tersedia. Saat Anda menggunakan:

var myVariable = {{ flaskvar | tojson }};

jika ada kesalahan yang menyebabkan variabel menjadi tidak ada, kesalahan yang dihasilkan dapat menghasilkan hasil yang tidak diharapkan. Untuk menghindari ini:

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

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

Ini menggunakan jinja2 untuk mengubah tupel geocode menjadi string json, lalu javascript JSON.parsemengubahnya menjadi larik javascript.


1
Mengapa Anda tidak membuat serial json dengan python
Mojimi

0

Nah, saya punya metode rumit untuk pekerjaan ini. Idenya adalah sebagai berikut-

Buat beberapa tag HTML yang tidak terlihat seperti <label>, <p>, <input>dll pada body HTML dan buat pola pada tag id, misalnya gunakan indeks daftar pada id tag dan nilai daftar pada nama kelas tag.

Di sini saya memiliki dua daftar maintenance_next [] dan maintenance_block_time [] dengan panjang yang sama. Saya ingin meneruskan dua data daftar ini ke javascript menggunakan flask. Jadi saya mengambil beberapa label label yang tidak terlihat dan menetapkan nama tagnya adalah pola indeks daftar dan menetapkan nama kelasnya sebagai nilai pada indeks.

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

Setelah ini, saya mengambil data dalam javascript menggunakan beberapa operasi javascript sederhana.

<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

Beberapa file js berasal dari web atau pustaka, mereka tidak dibuat sendiri. Kode yang mereka dapatkan variabel seperti ini:

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

Metode ini membuat file js tidak berubah (tetap independen), dan berikan variabel dengan benar!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.