An Interactive Web Dashboard with Plotly and Flask
By: Alan Jones
Tags: Ajax, Data Visualization, Flask, Dash, Plotly

Data visualization

An Interactive Web Dashboard with Plotly and Flask

To create a truly interactive app with Dash you need to use callbacks. You can achieve the same thing with Plotly and Flask. Alan Jones May 15·7 min read

The app and code — image by autho

It may seem as if I have something against Dash. Honestly, I haven’t; it’s a fine product, comes with a lot of support from Plotly and does what it is intended to do, well.

But, as I attempted to demonstrate in a previous article, there is no need to write HTML in Python (as Dash requires you to do) when you can write it in HTML! And, I would now add, there’s no need for additional layers of complexity in a Flask app — fundamentally, Dash is a Flask app — when a smidgen of Javascript and HTML will do the same job.

In my previous article I demonstrated that you can create a simple web app incorporating Plotly charts without Dash. Instead you can use a combination of Flask and a web page template. This also gives you the advantage to create multi-page apps.

But Dash also allows you to create apps that are interactive by loading new data into a chart using callback functions. So the question is can we easily do this with our Plotly plus Flask model? And, of course, the answer is ‘Yes’. Anything you can do with Dash you can also do with its underlying technologies: Python, Flask, HTML and Javascript.

Is it really easier, though?

That is for you to judge, but from my (possibly prejudiced) point of view it is. And if you need to learn just a tiny bit of HTML or Javascript that you have not come across before, then fine, you may find a use for it in the future.

To demonstrate this, we are going to very quickly run through a bare-bones web app that displays the GDP per capita for a range of countries. Where the interactivity comes in is that you can type in a new country name and the chart will automatically update by calling back to the server and reloading the chart with new data.

The app looks like this:

It’s based on data from the Gapminder Foundation that is included in the Plotly library and used in some of their example programs.

The app is really very small and quite simple but demonstrates the techniques that you can use (and gives you code you can reuse) to create impressive interactive dashboard apps. You will need to use a little HTML, a tiny bit of Javascript and know the very basics of how to create and run a Flask app (see here for a simple introduction: How to Create and Run a Flask App).

You can find a link to the code at the end of this article — download it as a zip file, or access it from my Github repository.

The Flask app

You can see in the code below that there are two routes defined: the root, /, on line 13 and callback on line 9. They both use data from the function gm (which does most of the work) but in different ways. The root path uses a template, chartsajax.html (which we will see below) and passes the data to it, whereas callback just returns the data itself. You can probably see where this is going.

This app is a single page that is rendered by the root path and chartsajax.html. This draws an initial plot but also allows the user to enter a new country name. When that name is entered a request is sent to the callback route and the data returned from that route is used to update the plot. The result is a pleasing update of the chart but without the blank screen that you would encounter if you were to reload the whole page.

The function gm on line 17 actually creates the plot. It takes a parameter, the name of the country that we want to plot. This parameter defaults to the value ‘United Kingdom’ to cater for the call from the root path where no parameter is passed. Then a Pandas DataFrame is created from the Gapminder dataset on line 18. From this we create the Plotly chart for the country.

Line 22 encodes the chart as JSON, using the encoder that comes with Plotly, as is required for it to be rendered in the HTML page, and this is then returned to the caller.

The HTML page

The HTML defines a simple layout that can be seen below in lines 17 to 23. A couple of headers are followed by a single paragraph, a text input field and then, the star of the show, the chart. Plotly needs a <div> tag in HTML in which to construct a chart and this it on line 22.

That’s the easy bit — not that the rest is very complicated.

When the page loads the libraries for Plotly and JQuery are loaded — we need Plotly to render the graph, of course, and we will use a JQuery function to implement the call back to the server. Next the Javascript function cb is defined — more on this later.

The body of the page is then loaded and following this the Javascript in lines 25 to 28 are executed for the first and only time. We can see in this code that a variable, d, is set to the value of the template variable graphJSON — this is the value that we passed to the template in the Flask app. Then the Plotly graph is plotted using the newPlot function from the Plotly Javascript library. This gives us our default plot.

Now, if we want to change the graph to display data for a different country, we simply type in the name of the country and hit the return key. The graph will update itself. (Note that if you type an invalid country, you’ll just get a blank graph.)

This works by using the onchange attribute in the input field on line 21. onchange does just what you would expect: when the value of the input field changes, the function cb is called with the value of the field.

The callback

That function, cb, is the callback function. It takes the value of the input field and sends it to the callback route via a JQuery function, getJSON. getJSON is an AJAX call which performs an asynchronous HTTP request to the specified URL, passing data to it, receiving a response (which it interprets as JSON encoded), and defining a function to be called when a successful result is received.

The success function takes the returned value — which is the JSON encoded graph — and calls the Plotly newPlot function to update the graph.

(AJAX, by the way stands for Asynchronous Javascript and XML — in other words an asynchronous HTTP request using Javascript that uses XML as the format for data transfer, except that, these days, JSON is the more common format for data transfer, not XML.)

I won’t go into the details of how the JQuery function works, you can either just accept this as boilerplate code that you can reuse, or learn more about JQuery here. What is useful to know about, however, is the data parameter. This looks like this

data: { ‘data’: selection }

and it is the data that will be sent to the callback function in the Flask app. data is the name of the parameter that is being sent and selection is it’s value (as passed from the text input field).

What next

As promised, that was a very quick run through the design of a simple interactive web app using Flask, Plotly and a AJAX. I hope, along with “Web Visualization with Plotly and Flask” it has demonstrated that you can easily build interactive dashboards with the technologies that underlie Dash, and that it is not such a daunting task to do so.

There are other techniques that the Dash tutorials demonstrate more sophisticated behaviours and, of course, these can be implemented using similar techniques to the ones I have demonstrated. But that is something for another day.

Thanks for reading and I hope you have found this useful. If you’d like to know when I publish other articles, please consider subscribing to my free and occasional newsletter, here.

UPDATE: In the code, that you can download below, I have included a new version that only uses Ajax for loading the chart, i.e. it does not use a parameter passed to the template to load the initial chart. It also labels the chart with the country that it represents a bit of an improvement, I think — see the README file for details.

Get the code

See the code in my code repository or download the zip file for this, and the previous article, on the Github page here.