Comment puis-je transmettre des données de Flask à JavaScript dans un modèle?

124

Mon application appelle une API qui renvoie un dictionnaire. Je souhaite transmettre les informations de ce dict à JavaScript dans la vue. J'utilise l'API Google Maps dans le JS, en particulier, je voudrais donc lui transmettre une liste de tuples avec les informations long / lat. Je sais que render_templatecela transmettra ces variables à la vue afin qu'elles puissent être utilisées en HTML, mais comment pourrais-je les transmettre à JavaScript dans le modèle?

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

Réponses:

141

Vous pouvez utiliser {{ variable }}n'importe où dans votre modèle, pas seulement dans la partie HTML. Donc, cela devrait fonctionner:

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

Considérez-le comme un processus en deux étapes: Premièrement, Jinja (le moteur de modèle utilisé par Flask) génère votre sortie texte. Ceci est envoyé à l'utilisateur qui exécute le JavaScript qu'il voit. Si vous souhaitez que votre variable Flask soit disponible en JavaScript sous forme de tableau, vous devez générer une définition de tableau dans votre sortie:

<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 propose également des constructions plus avancées de Python, vous pouvez donc les raccourcir en:

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

Vous pouvez également utiliser des forboucles, des ifinstructions et bien d'autres, voir la documentation Jinja2 pour plus.

Jetez également un œil à la réponse de Ford qui souligne le tojsonfiltre qui est un ajout à l'ensemble standard de filtres de Jinja2 .

Edit Nov 2018: tojsonest maintenant inclus dans l'ensemble standard de filtres de Jinja2.

mensi
la source
2
Bien obligé, mensi! C'était ma première pensée, mais la documentation de Flask ne dit pas clairement que vous pouvez également utiliser le formulaire {{var}} dans JS. Merci d'avoir clarifié cela.
mea
2
@mea: vous pouvez également utiliser le moteur de modèle pour générer des fichiers texte arbitraires, je l'ai également utilisé pour générer dynamiquement des fichiers TeX (-> PDF) et des e-mails, c'est assez polyvalent;)
mensi
Question de suivi rapide: si je fais une boucle for dans JS, puis-je utiliser la variable index dans la variable python, par exemple {{geocode [i]}}?
me
1
Cela a du sens, mais la solution que vous avez publiée semble m'obliger à coder manuellement le contenu du tableau JS. J'espérais pouvoir le faire de manière plus programmatique afin de pouvoir passer une liste Python de longueur variable et une boucle JS for pourrait itérer sur la longueur, puis les ajouter au tableau JS. Désolé si je ne suis pas assez clair, mais je suis plutôt vert envers JS et les développeurs web.
me
1
@RocketPingu si vous souhaitez transmettre des données dans un fichier séparé, il est généralement plus facile d'utiliser simplement le jsonmodule pour vider vos données sous la forme d'un objet json
mensi
114

Le moyen idéal pour obtenir à peu près n'importe quel objet Python dans un objet JavaScript est d'utiliser JSON. JSON est un excellent format pour le transfert entre les systèmes, mais nous oublions parfois qu'il signifie JavaScript Object Notation. Cela signifie que l'injection de JSON dans le modèle est identique à l'injection de code JavaScript qui décrit l'objet.

Flask fournit un filtre Jinja pour cela: tojsonvide la structure dans une chaîne JSON et la marque comme sûre afin que Jinja ne l'échappe pas automatiquement.

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

Cela fonctionne pour toute structure Python sérialisable 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);
gué
la source
4
Essayez ceci la prochaine fois que vous entrerez Uncaught SyntaxError: Unexpected token &dans la console javascript.
scharfmn
8
Je trouve cette réponse plus solide et logique que la réponse acceptée.
Konrad
J'ai eu une erreur lors de l'exécution du code:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13
27

L'utilisation d'un attribut de données sur un élément HTML évite d'avoir à utiliser des scripts en ligne, ce qui signifie que vous pouvez utiliser des règles CSP plus strictes pour une sécurité accrue.

Spécifiez un attribut de données comme ceci:

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

Ensuite, accédez-y dans un fichier JavaScript statique comme ceci:

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

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

Vous pouvez également ajouter un point de terminaison pour renvoyer votre variable:

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

Ensuite, faites un XHR pour le récupérer:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })
Vinnie James
la source
Oui, vous pouvez , et dans certaines architectures (en particulier les SPA), c'est la bonne façon de faire les choses, mais gardez à l'esprit qu'il y a plusieurs inconvénients à faire cela par rapport à l'intégration des données dans la page lorsque vous la servez: 1. c'est plus lent, 2. il nécessite un peu plus de code même pour le faire avec négligence, et 3. il introduit au moins deux états frontaux supplémentaires que vous devrez probablement gérer proprement dans votre interface utilisateur (à savoir l'état où la requête XHR est toujours en cours) , et celui où il a complètement échoué), ce qui nécessite un tas de code JavaScript supplémentaire et introduit une source supplémentaire de bogues potentiels.
Mark Amery
3

Juste une autre solution alternative pour ceux qui veulent passer des variables à un script qui provient de flask, j'ai seulement réussi à faire fonctionner cela en définissant les variables à l'extérieur, puis en appelant le script comme suit:

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

Si j'entre des variables jinja, test123.jscela ne fonctionne pas et vous obtiendrez une erreur.

Tsando
la source
Exactement ce que je cherchais.
shrishinde
1
-1; cette réponse n'a pas de sens. Je pense (sur la base du libellé "un script qui provient de flask" et de votre attente apparente de pouvoir utiliser des variables de modèle dans /static/test123.js) que vous ne comprenez pas comment <script>s avecsrc . Ce n'est pas une fonctionnalité Flask. Le navigateur , lorsqu'il analyse un tel script, fait une requête HTTP distincte pour obtenir le script. Le contenu du script n'est pas intégré au modèle par Flask; en effet, Flask a probablement fini d'envoyer le modèle HTML au navigateur au moment où le navigateur demande même le script.
Mark Amery
3

Des réponses de travail sont déjà données, mais je souhaite ajouter une vérification qui agit comme une sécurité intégrée au cas où la variable flask ne serait pas disponible. Lorsque vous utilisez:

var myVariable = {{ flaskvar | tojson }};

s'il y a une erreur qui rend la variable inexistante, les erreurs qui en résultent peuvent produire des résultats inattendus. Pour éviter cela:

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

Cela utilise jinja2 pour transformer le tuple de géocodage en une chaîne json, puis le javascript le JSON.parsetransforme en un tableau javascript.

Nackjicholson
la source
1
Pourquoi ne pas sérialiser simplement le json en python
Mojimi
0

Eh bien, j'ai une méthode délicate pour ce travail. L'idée est la suivante:

Créez des balises HTML invisibles comme <label>, <p>, <input>etc. dans le corps HTML et créez un modèle dans l'identifiant de la balise, par exemple, utilisez l'index de liste dans l'identifiant de la balise et la valeur de la liste au nom de la classe de balise.

Ici, j'ai deux listes maintenance_next [] et maintenance_block_time [] de même longueur. Je veux transmettre les données de ces deux listes à javascript en utilisant le flacon. Je prends donc une étiquette d'étiquette invisible et définit son nom de balise comme un modèle d'index de liste et définit son nom de classe comme valeur à l'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%}

Après cela, je récupère les données en javascript en utilisant une simple opération 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>

Tanmoy Datta
la source
-1

Certains fichiers js proviennent du Web ou de la bibliothèque, ils ne sont pas écrits par vous-même. Le code qu'ils obtiennent variable comme ceci:

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

Cette méthode rend les fichiers js inchangés (garde l'indépendance) et passe la variable correctement!

Jayzhen
la source