Search code examples
javascriptpythonplotlyfastapiplotly.js

FastAPI: How to handle Plotly graph click events in a web application?


I am using FastAPI to create a simple web app where I want to render a Plotly graph and record the (x, y) values when the graph is clicked. I am exporting the graph and then trying to set up an event handler for clicks.

Here is the relevant part of my FastAPI code:

    new_html = """
    <div id='divPlotly'></div>
    <script>
        var plotly_data = {}
        Plotly.react('divPlotly', plotly_data.data, plotly_data.layout);
    </script>
    """.format(
    fig.to_json())
    return templates.TemplateResponse(
        "graph.html", {"request": request, "graph_html": new_html}
    )

I also tried exporting directly to HTML:

graph_html = fig.to_html(full_html=False)
return templates.TemplateResponse(
    "graph.html", {"request": request, "graph_html": graph_html}
)

My HTML file (graph.html) looks like this:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Graph</title>
        <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
    </head>
    <body>
        <div id="graph-container">
            {{ graph_html | safe }}
        </div>
        <script>
            // Function to initialize Plotly graph click event handler
            function initGraphClickHandler() {
                var graphContainers = document.getElementsByClassName('plot-container plotly');
                console.log(graphContainers);
                if (graphContainers.length > 0) {
                    var graphContainer = graphContainers[0];
                    console.log(graphContainer);
                    graphContainer.on('plotly_click', function(data) {
                        var point = data.points[0];
                        var x = point.x;
                        var y = point.y;
                        var text = `Clicked Point: X=${x}, Y=${y}`;
                        alert(text);
                    });
                }
            }
            document.addEventListener('DOMContentLoaded', initGraphClickHandler);
        </script>
    </body>
    </html>

However, I am encountering the following error in the console:

    TypeError: graphContainer.on is not a function
        at initGraphClickHandler (visualize:88:36)
        at Object.success (visualize:49:25)
        at c (jquery-3.6.0.min.js:2:28327)
        at Object.fireWith [as resolveWith] (jquery-3.6.0.min.js:2:29072)
        at l (jquery-3.6.0.min.js:2:79901)
        at XMLHttpRequest.<anonymous> (jquery-3.6.0.min.js:2:82355)

What I've Tried:

Exporting the graph to JSON and then using Plotly.react. Directly exporting the graph to HTML using fig.to_html. Using different variations of accessing the graph container and setting up the event listener. Problem: The error TypeError: graphContainer.on is not a function suggests that the graphContainer element does not have the .on method. I'm not sure if I'm accessing the correct element or if there's an issue with how I'm initializing Plotly.

Question: How can I correctly set up a click event handler for my Plotly graph within this FastAPI application? Any help or suggestions would be greatly appreciated. Thanks!


Solution

  • The thing is that the graph div is still unprocessed when the DOMContentLoaded event fires (and the on method hasn't been registered on the DOM object when initGraphClickHandler() runs).

    Fortunately, Plotly.react() returns a promise that resolves when the graph div is "ready", and the success callback receives it as a parameter, so you can add the plotly_click handler at the right moment using then() and without having to query the DOM element.

    For example (directly in the FastAPI part of the code) :

    Plotly.react(...).then(gd => gd.on('plotly_click', clickHandler);
    

    NB. Don't use plotly-latest.js or its minified version anymore. While still referenced here and there in the documentation, they are stuck at version 1.58.5.

    Please note that as of v2 the "plotly-latest" outputs will no longer be updated on the CDN, and will stay at the last v1 patch v1.58.5. Therefore, to use the CDN with plotly.js v2 and higher, you must specify an exact plotly.js version.

    For example :

    <script src="https://cdn.plot.ly/plotly-2.34.0.js"></script>