Bir şablonda Flask'tan JavaScript'e nasıl veri aktarabilirim?


124

Uygulamam, bir sözlük döndüren bir API'ye çağrı yapıyor. Görünümde bu dikteden JavaScript'e bilgi aktarmak istiyorum. Özellikle JS'de Google Haritalar API'sini kullanıyorum, bu nedenle ona uzun / enlem bilgilerini içeren bir tuple listesi geçirmek istiyorum. Bunun render_templatebu değişkenleri HTML'de kullanılabilmeleri için görünüme aktaracağını biliyorum , ancak bunları şablondaki JavaScript'e nasıl aktarabilirim?

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)

Yanıtlar:


142

Yalnızca {{ variable }}HTML bölümünde değil, şablonunuzun herhangi bir yerinde kullanabilirsiniz . Yani bu işe yaramalı:

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

Bunu iki aşamalı bir süreç olarak düşünün: İlk olarak, Jinja (Flask'ın kullandığı şablon motoru) metin çıktınızı oluşturur. Bu, gördüğü JavaScript'i çalıştıran kullanıcıya gönderilir. Flask değişkeninizin JavaScript'te bir dizi olarak kullanılabilmesini istiyorsanız, çıktınızda bir dizi tanımı oluşturmanız gerekir:

<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 ayrıca Python'dan daha gelişmiş yapılar sunar, böylece kısaltabilirsiniz:

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

Ayrıca fordöngüleri, ififadeleri ve daha fazlasını kullanabilirsiniz , daha fazlası için Jinja2 belgelerine bakın.

Ayrıca, Jinja2'nin standart filtre setinetojson ek olan filtreyi işaret eden Ford'un cevabına bir göz atın .

Kasım 2018'i Düzenle: tojsonartık Jinja2'nin standart filtre setine dahil edildi.


2
Çok minnettarım mensi! Bu benim ilk düşüncemdi, ancak Flask dokümantasyonu JS'de de {{var}} formunu kullanabileceğinizi açıkça göstermiyor. Bunu açıklığa kavuşturduğunuz için teşekkürler.
mea

2
@mea: şablon motorunu rastgele metin tabanlı dosyalar oluşturmak için de kullanabilirsiniz, bunu dinamik olarak TeX dosyaları (-> PDF) ve e-posta oluşturmak için de kullandım, oldukça çok yönlü;)
mensi

hızlı takip sorusu: JS'de bir for döngüsü yaparsam, indeks değişkenini python değişkeni içinde kullanabilir miyim, örneğin {{geocode [i]}}?
mea

1
Bu mantıklı, ancak yayınladığınız çözüm, JS dizisinin içeriğine elle kod yazmamı gerektiriyor gibi görünüyor. Bunu daha programlı bir şekilde yapabileceğimi umuyordum, böylece değişken uzunluklu bir Python listesi geçirebilirim ve bir JS for döngüsü uzunluk boyunca yineleyebilir ve ardından bunları JS dizisine ekleyebilir. Kendimi yeterince netleştiremiyorsam özür dilerim, ancak JS ve web geliştirmeye karşı daha yeşilim.
mea

1
@RocketPingu Verileri ayrı bir dosyaya aktarmak istiyorsanız json, verilerinizi bir json nesnesi biçiminde dökmek için modülü kullanmak genellikle daha kolaydır
mensi

114

Hemen hemen her Python nesnesini bir JavaScript nesnesine sokmanın ideal yolu JSON kullanmaktır. JSON, sistemler arasında aktarım biçimi olarak harikadır, ancak bazen JavaScript Nesne Gösterimi anlamına geldiğini unutuyoruz. Bu, şablona JSON enjekte etmenin, nesneyi tanımlayan JavaScript kodunu enjekte etmekle aynı olduğu anlamına gelir.

Flask bunun için bir Jinja filtresi sağlar: tojsonyapıyı bir JSON dizesine döker ve Jinja'nın otomatik olarak düzenlemesini yapmaması için güvenli olarak işaretler.

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

Bu, JSON serileştirilebilir olan herhangi bir Python yapısı için çalışır:

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
Uncaught SyntaxError: Unexpected token &Javascript konsoluna bir sonraki girişinizde bunu deneyin .
scharfmn

8
Bu cevabı, kabul edilen cevaptan daha sağlam ve mantıklı buluyorum.
Konrad

Kodu çalıştırırken bir hata aldım:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

Bir HTML öğesinde bir veri özniteliğinin kullanılması, satır içi komut dosyası kullanma zorunluluğunu ortadan kaldırır, bu da daha fazla güvenlik için daha katı CSP kuralları kullanabileceğiniz anlamına gelir .

Şu şekilde bir veri özelliği belirtin:

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

Ardından buna statik bir JavaScript dosyasında aşağıdaki gibi erişin:

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

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

11

Alternatif olarak, değişkeninizi döndürmek için bir uç nokta ekleyebilirsiniz:

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

Ardından onu almak için bir XHR yapın:

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

Evet, yapabilir ve bazı mimarilerde (özellikle SPA'lar) bu, işleri yapmanın doğru yoludur, ancak bunu yapmanın, veriyi sunarken sayfaya veri pişirmeye kıyasla birkaç dezavantajı olduğunu unutmayın: 1. bu daha yavaş, 2. Dikkatsizce yapmak için bile biraz daha fazla kod gerektirir ve 3. Kullanıcı arayüzünüzde muhtemelen temiz bir şekilde işlemeniz gerekecek en az iki ekstra ön uç durumu (yani XHR isteğinin hala uçuşta olduğu durum) sunar ve tamamen başarısız olduğu yer), bu da bir sürü ekstra JavaScript kodu gerektirir ve ekstra bir potansiyel hata kaynağı sağlar.
Mark Amery

3

Değişkenleri flask kullanılarak elde edilen bir betiğe geçirmek isteyenler için bir başka alternatif çözüm, bunu yalnızca dışarıdaki değişkenleri tanımlayarak ve ardından betiği aşağıdaki gibi çağırarak çalıştırmayı başardım:

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

Eğer jinja değişkenlerini girersem test123.jsçalışmaz ve bir hata alırsınız.


Tam olarak aradığım şey.
shrishinde

1
1; bu cevap mantıklı değil. Sanırım ( "şişe kullanılarak elde edilen bir komut dosyası" ifadesine ve şablon değişkenlerini kullanabileceğinize dair açık beklentinize dayanarak /static/test123.js) <script>s ile nasıl çalıştığını yanlış anladığınızı düşünüyorum src. Flask özelliği değiller. Tarayıcı böyle bir senaryoyu ayrıştırır zaman, komut dosyası almak için ayrı bir HTTP isteğinde bulunur. Komut dosyası içeriği, Flask tarafından şablona eklenmez; gerçekten de, tarayıcının komut dosyasını istediği anda Flask şablonlu HTML'yi tarayıcıya göndermeyi büyük olasılıkla bitirdi.
Mark Amery

3

Çalışma cevapları zaten verilmiştir, ancak flask değişkeninin mevcut olmaması durumunda hata korumalı olarak davranan bir kontrol eklemek istiyorum. Kullandığınızda:

var myVariable = {{ flaskvar | tojson }};

Değişkenin var olmamasına neden olan bir hata varsa, ortaya çıkan hatalar beklenmedik sonuçlar doğurabilir. Bundan kaçınmak için:

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

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

Bu, geocode demetini bir json dizesine dönüştürmek için jinja2'yi kullanır ve ardından javascript JSON.parsebunu bir javascript dizisine dönüştürür.


1

0

Bu iş için aldatıcı bir yöntemim var. Fikir şu şekildedir:

<label>, <p>, <input>HTML gövdesinde vb. Gibi bazı görünmez HTML etiketleri oluşturun ve etiket kimliğinde bir kalıp oluşturun, örneğin, etiket kimliğinde liste dizini ve etiket sınıfı adında liste değerini kullanın.

Burada aynı uzunlukta care_next [] ve care_block_time [] olmak üzere iki liste var. Bu iki listenin verilerini şişeyi kullanarak javascript'e geçirmek istiyorum. Bu yüzden bazı görünmez etiket etiketlerini alıyorum ve etiket adını bir liste dizini kalıbı olarak ayarlıyorum ve sınıf adını dizindeki değer olarak ayarlıyorum.

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

Bundan sonra, bazı basit javascript işlemlerini kullanarak javascript'teki verileri alıyorum.

<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

Bazı js dosyaları web veya kitaplıktan gelir, kendi başınıza yazılmazlar. Değişken aldıkları kod şöyle:

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

Bu yöntem js dosyalarını değiştirmez (bağımsızlığı korur) ve değişkeni doğru şekilde geçirir!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.