2

I want to know what the best practice is for my application project.

I want to render a chart using Chart.js. Data is generated in Python, and I know how to pass it to html: render_template("/index.html", chart_data=chart_data). However, after passing chart_data to html, I don't know how to pass it to the javascript, where the chart is generated. Or, is it better to pass it directly from .py to .js, without bypassing .html? If so, what is the way?

Here are my codes: app.py

import os
from flask import Flask, flash, redirect, render_template, request, session
from flask_session import Session

# Configure application
app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    chart_data = {
        "labels": ["Italy", "France", "Spain", "USA", "Argentina"],
        "values": [55, 49, 44, 24, 15]
    }
    return render_template("/index.html", chart_data=chart_data)


if __name__ == "__main__":
    app.run()

index.html

<!DOCTYPE html>

<html lang="en">

<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
</head>

<body>
    <div class="container">
        <canvas id="myChart" style="width:100%;max-width:700px"></canvas>
        <script type="text/javascript" src="static/app.js"></script>
    </div>
</body>

</html>

app.js

// For the testing purpose, I generated the same data in js. 
// But I want to fetch this from .html, or directly from .py.
var xValues = ["Italy", "France", "Spain", "USA", "Argentina"];
var yValues = [55, 49, 44, 24, 15];
var barColors = ["red", "green", "blue", "orange", "brown"];

let myChart = document.getElementById('myChart').getContext('2d');

let barChart = new Chart(myChart, {
    type: 'bar',
    data: {
        labels: xValues,
        datasets: [
            {
                label: "population",
                data: yValues
            }
        ]
    },
    options: {}
});

As a side note, this is a minimal reproducible example from my entire project. In the real project, my app.py gets user inputs from index.html using request.form.get() and dynamically generates chart_data. But I excluded this part from the question because I was able to make it work.

EDIT In the app.js, I modified like below but it seems this syntax is only supported in .html. I don't want to include my js script in html, but instead have it as a separated .js file.

data: {
        labels: {{ chart_data['labels']| tojson }},
    datasets: [
        {
            label: "population",
            data: {{ chart_data['values']| tojson }}
                }
    ]
}
Makoto Miyazaki
  • 1,743
  • 2
  • 23
  • 39
  • Does this answer your question? [How could I pass data to chart.js with flask?](https://stackoverflow.com/questions/64874084/how-could-i-pass-data-to-chart-js-with-flask) – LeeLenalee Dec 19 '21 at 11:45
  • thanks for the direction. Yes it answers but partly. What if I don't want to include my javascript code in `.html` file but instead have a separated `.js` file? I modified my `.js`file to `labels: {{ chart_data['labels']| tojson }}` and `data: {{ chart_data['values']| tojson }}` but it seems this syntax is only supported in html fiels. – Makoto Miyazaki Dec 19 '21 at 12:19
  • 1
    That's what APIs are for. – Jishan Shaikh Dec 19 '21 at 12:44
  • I don't understand your answer. Could you elaborate more? @JishanShaikh – Makoto Miyazaki Dec 19 '21 at 12:57
  • Instead of posting your data from python to HTML, you can directly transfer the data (preferably in JSON) to JavaScript using a custom API. You'll have to write an API and host it. – Jishan Shaikh Dec 19 '21 at 13:04
  • @Jishan Flask is already running an API – OneCricketeer Jan 08 '22 at 15:40
  • @Mako You can use ` – OneCricketeer Jan 08 '22 at 15:42

1 Answers1

1

The data should come from HTTP calls; doesn't really matter where that is, but it's not directly from HTML or a ".py file". Since you're using Flask and the data could be dynamic, you would add a route for it, not render it statically

# assume this is defined somewhere else, like a database 
chart_data = {
        "labels": ["Italy", "France", "Spain", "USA", "Argentina"],
        "values": [55, 49, 44, 24, 15]
    } 

@app.route("/data", methods=["GET"])
def data():
    global chart_data
    return jsonify(chart_data)

Then, you need a JS file that is loaded to call fetch API to get the data, and build the chart

let myChart = document.getElementById('myChart').getContext('2d');

fetch('http://127.0.0.1/data')
  .then(response => response.json())
  .then(data => {
       console.log(data);
       let labels = data.labels; 
       let barChart = new Chart(myChart, {  
           ... 
   });

Then you include the JS file in a template

<script type="text/javascript" src="{{ url_for('static', filename='app.js'}}"/>

Link to Flask static files with url_for

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245