154

My app makes a call to an API that returns a dictionary. I want to pass information from this dict to JavaScript in the view. I am using the Google Maps API in the JS, specifically, so I'd like to pass it a list of tuples with the long/lat information. I know that render_template will pass these variables to the view so they can be used in HTML, but how could I pass them to JavaScript in the 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)
0

10 Answers 10

184

You can use {{ variable }} anywhere in your template, not just in the HTML part. So this should work:

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

Think of it as a two-stage process: First, Jinja (the template engine Flask uses) generates your text output. This gets sent to the user who executes the JavaScript he sees. If you want your Flask variable to be available in JavaScript as an array, you have to generate an array definition in your output:

<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 also offers more advanced constructs from Python, so you can shorten it to:

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

You can also use for loops, if statements and many more, see the Jinja2 documentation for more.

Also, have a look at Ford's answer who points out the tojson filter which is an addition to Jinja2's standard set of filters.

Edit Nov 2018: tojson is now included in Jinja2's standard set of filters.

12
  • 2
    Much obliged, mensi! That was my initial thought but the documentation for Flask doesn't make it readily clear that you can use the {{var}} form in JS as well. Thanks for clearing that up.
    – mea
    Jun 24, 2012 at 15:13
  • 2
    @mea: you can also use the template engine to generate arbitrary text-based files, I have also used it to dynamically generate TeX files (-> PDF) and email, it's quite versatile ;)
    – mensi
    Jun 24, 2012 at 15:17
  • 1
    quick follow up question: if I do a for loop in JS, can I use the index variable within the python variable e.g. {{geocode[i]}} ?
    – mea
    Jun 25, 2012 at 15:51
  • 2
    That makes sense, but the solution you posted seems to require me to hand-code in the contents of the JS array. I was hoping I could do it more programmatically such that I could pass a Python list of variable length and a JS for loop could iterate over the length and then append them to the JS array. Sorry if I am not making myself clear enough, but I am rather green to JS and web dev.
    – mea
    Jun 25, 2012 at 16:40
  • 2
    @RocketPingu if you want to pass data in a separate file, it's usually easier to just use the jsonmodule to dump your data in the form of a json object
    – mensi
    Mar 17, 2018 at 16:30
134

The ideal way to go about getting pretty much any Python object into a JavaScript object is to use JSON. JSON is great as a format for transfer between systems, but sometimes we forget that it stands for JavaScript Object Notation. This means that injecting JSON into the template is the same as injecting JavaScript code that describes the object.

Flask provides a Jinja filter for this: tojson dumps the structure to a JSON string and marks it safe so that Jinja does not autoescape it.

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

This works for any Python structure that is JSON serializable:

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);
3
  • 4
    Try this the next time you get Uncaught SyntaxError: Unexpected token & in the javascript console.
    – scharfmn
    Jan 5, 2015 at 23:49
  • 9
    I find this answer more solid and logical than the accepted answer.
    – Konrad
    Mar 22, 2018 at 17:10
  • 1
    I got an error running the code: TypeError: Object of type Undefined is not JSON serializable
    – jbuddy_13
    May 25, 2020 at 19:05
37

Using a data attribute on an HTML element avoids having to use inline scripting, which in turn means you can use stricter CSP rules for increased security.

Specify a data attribute like so:

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

Then access it in a static JavaScript file like so:

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

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

Alternatively you could add an endpoint to return your variable:

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

Then do an XHR to retrieve it:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })
1
  • 2
    Yes, you could, and in some architectures (esp. SPAs) this is the proper way to do things, but bear in mind that there are several disadvantages to doing this versus baking the data into the page when you serve it: 1. it's slower, 2. it requires slightly more code even to do sloppily, and 3. it introduces at least two extra front-end states that you will likely need to handle cleanly in your UI (namely the state where the XHR request is still in flight, and the one where it's failed completely), which requires a bunch of extra JavaScript code and introduces an extra source of potential bugs.
    – Mark Amery
    Dec 27, 2018 at 20:16
6

Working answers are already given but I want to add a check that acts as a fail-safe in case the flask variable is not available. When you use:

var myVariable = {{ flaskvar | tojson }};

if there is an error that causes the variable to be non existent, resulting errors may produce unexpected results. To avoid this:

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

Just another alternative solution for those who want to pass variables to a script which is sourced using flask, I only managed to get this working by defining the variables outside and then calling the script as follows:

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

If I input jinja variables in test123.js it doesn't work and you will get an error.

1
  • 2
    -1; this answer doesn't make sense. I think (based on the phrasing "a script which is sourced using flask" and your apparent expectation that you'd be able to use template variables in /static/test123.js) that you're misunderstanding how <script>s with srcs work. They're not a Flask feature. The browser, when it parses such a script, makes a separate HTTP request to get the script. The script content doesn't get baked into the template by Flask; indeed, Flask has likely finished sending the templated HTML to the browser by the time the browser even requests the script.
    – Mark Amery
    Dec 27, 2018 at 20:21
3
<script>
    const geocodeArr = JSON.parse('{{ geocode | tojson }}');
    console.log(geocodeArr);
</script>

This uses jinja2 to turn the geocode tuple into a json string, and then the javascript JSON.parse turns that into a javascript array.

0
1

Well, I have a tricky method for this job. The idea is as follow-

Make some invisible HTML tags like <label>, <p>, <input> etc. in HTML body and make a pattern in tag id, for example, use list index in tag id and list value at tag class name.

Here I have two lists maintenance_next[] and maintenance_block_time[] of the same length. I want to pass these two list's data to javascript using the flask. So I take some invisible label tag and set its tag name is a pattern of list index and set its class name as value at 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%}

After this, I retrieve the data in javascript using some simple javascript operation.

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

0

Awesome thread! This helped a lot! Here is my finished pseudo-code:

mypage.html

<script>
    var user = {{username}}
</script>

mypage.js

console.log('username = ' + user)
-1

Some js files come from the web or library, they are not written by yourself. The code they get variable like this:

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

This method makes js files unchanged(keep independence), and pass variable correctly!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.