Як я можу передавати дані з колби в JavaScript у шаблоні?


123

Мій додаток телефонує в API, який повертає словник. Я хочу передати інформацію з цього диктату на перегляд у JavaScript. Я використовую API Карт Google в JS, зокрема, тому я хотів би передати йому список кортежів з довгою / лат інформацією. Я знаю, що render_templateпередасть ці змінні до перегляду, щоб їх можна було використовувати в HTML, але як я можу передати їх JavaScript у шаблоні?

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)

Відповіді:


140

Ви можете використовувати {{ variable }}будь-де у своєму шаблоні, а не лише в частині HTML. Отже, це має працювати:

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

Розгляньте це як двоступеневий процес: по-перше, Jinja (механізм шаблонів, який використовує колба) генерує ваш текст. Це надсилається користувачеві, який виконує JavaScript, який він бачить. Якщо ви хочете, щоб ваша змінна Flask була доступна в JavaScript як масив, ви повинні генерувати визначення масиву у своєму виході:

<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 також пропонує більш вдосконалені конструкції від Python, так що ви можете скоротити його до:

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

Ви також можете використовувати forпетлі, ifзаяви та багато іншого, докладнішу інформацію див. У документації Jinja2 .

Крім того, подивіться на відповідь Ford, який вказує на tojsonфільтр, який є доповненням до стандартного набору фільтрів Jinja2 .

Редагувати листопад 2018 року: tojsonтепер включено до стандартного набору фільтрів Jinja2.


2
Дуже зобов’язаний, менсі! Це було моєю первинною думкою, але в документації на Flask не було чітко зрозуміло, що ви можете також використовувати форму {{var}} в JS. Дякуємо, що очистили це.
mea

2
@mea: ви також можете використовувати механізм шаблонів для генерації довільних текстових файлів, я також використовував його для динамічного генерування файлів TeX (-> PDF) та електронної пошти, це досить універсально;)
mensi

швидке наступне запитання: якщо я роблю цикл для JS, чи можу я використовувати змінну індексу в змінній python, наприклад {{geocode [i]}}?
mea

1
Це має сенс, але рішення, яке ви опублікували, здається, вимагає від мене коду вручну у вмісті масиву JS. Я сподівався, що я можу це зробити більш програмно, щоб я міг передати список Python змінної довжини, а JS для циклу міг перебирати довжину, а потім додавати їх до масиву JS. Вибачте, якщо я не роблю себе достатньо зрозумілим, але я досить зелений до JS та веб-розробників.
mea

1
@RocketPingu, якщо ви хочете передати дані в окремий файл, зазвичай простіше просто використовувати jsonмодуль, щоб скинути ваші дані у вигляді об’єкта json
mensi

113

Ідеальний спосіб отримати майже будь-який об’єкт Python в об’єкт JavaScript - використовувати JSON. JSON чудово підходить як формат для передачі між системами, але іноді ми забуваємо, що він розшифровується як JavaScript Object Notation. Це означає, що введення JSON в шаблон є тим же, що і введення коду JavaScript, який описує об’єкт.

Колба надає для цього фільтр Jinja: tojsonскидає структуру на рядок JSON і позначає її безпечною, щоб Jinja не автоматизував її.

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

Це працює для будь-якої структури Python, яку можна 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
Спробуйте це наступного разу, коли ви потрапите Uncaught SyntaxError: Unexpected token &в консоль javascript.
scharfmn

8
Я вважаю цю відповідь більш твердою та логічною, ніж прийнята відповідь.
Конрад

Я отримав помилку під керуванням коду:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

Використання атрибута даних на елементі HTML дозволяє уникнути використання вбудованого сценарію, що, в свою чергу, означає, що ви можете використовувати більш жорсткі правила CSP для підвищення безпеки.

Вкажіть атрибут даних так:

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

Потім перейдіть до нього в статичний файл JavaScript так:

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

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

11

Можна також додати кінцеву точку для повернення змінної:

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

Потім зробіть XHR, щоб отримати його:

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

Так, ви можете , а в деяких архітектурах (особливо SPA) це правильний спосіб робити речі, але майте на увазі, що для цього є кілька недоліків, порівняно з виведенням даних на сторінку, коли ви їх обслуговуєте: 1. це повільніше, 2. йому потрібно трохи більше коду, навіть щоб зробити це неохайно, і 3. він вводить щонайменше два додаткових передніх стани, з якими вам, ймовірно, потрібно буде працювати чисто у вашому інтерфейсі (а саме в стані, коли запит XHR все ще знаходиться в польоті , і той, де це повністю не вдалося), який вимагає купу додаткового коду JavaScript і вводить додаткове джерело потенційних помилок.
Марк Амері

3

Ще одне альтернативне рішення для тих, хто хоче передати змінні скрипту, який розміщується за допомогою колби, мені вдалося лише зробити це робочим шляхом, визначивши змінні зовні, а потім викликавши сценарій так:

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

Якщо я введіть змінні jinja, test123.jsвона не працює, і ви отримаєте помилку.


Саме те, що я шукав.
shrishinde

1
-1; ця відповідь не має сенсу. Я думаю (виходячи з фрази "сценарій, який розміщується за допомогою колби", і ваше очевидне сподівання, що ви зможете використовувати змінні шаблону в /static/test123.js), що ви нерозумієте, як працювати <script>з srcs. Вони не є колбою. Коли браузер аналізує такий сценарій, браузер робить окремий запит HTTP, щоб отримати скрипт. Вміст сценарію не потрапляє у шаблон Flask; Дійсно, Flask, ймовірно, закінчив надсилати шаблонний HTML до браузера до моменту, коли браузер навіть запитує сценарій.
Марк Амерді

3

Робочі відповіді вже наведені, але я хочу додати перевірку, яка діє як захист від відмов, якщо змінна колба недоступна. Під час використання:

var myVariable = {{ flaskvar | tojson }};

якщо є помилка, яка призводить до відсутності змінної, внаслідок цього помилки можуть призвести до несподіваних результатів. Щоб цього уникнути:

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

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

При цьому використовується jinja2 для перетворення кордону геокоду в рядок json, а потім javascript JSON.parseперетворює його в масив javascript.


1
Чому б ти просто не серіалізував json в python
Мохімі

0

Ну, я маю складний метод для цієї роботи. Ідея така:

Створіть кілька невидимих ​​тегів HTML, наприклад <label>, <p>, <input>тощо, у тілі HTML та зробіть шаблон у ідентифікаторі тегів, наприклад, використовуйте індекс списку в ідентифікаторі тегу та значення списку у назві класу тегів.

Тут у мене є два списки підтримання_наступного [] та обслуговування_блока_часа [] однакової довжини. Я хочу передати дані цих двох списків у javascript за допомогою колби. Тож я беру якийсь невидимий тег мітки і встановлюю його тег ім'я - зразок індексу списку і встановлюю його ім'я класу як значення в індексі.

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

Після цього я отримую дані в JavaScript за допомогою простої операції з 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

Деякі файли js надходять з Інтернету чи бібліотеки, вони не записуються власноруч. Код, який вони отримують такою змінною:

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

Цей метод робить файли js незмінними (зберігайте незалежність) та передайте змінну правильно!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.