Update dynamically created figure y-axis based on rangeselector

Hi,

Welcome to the community! :slight_smile:

This sounds like a tough problem… So let me start with the easy part:

for example, the callback has to define the id of the figure in the callback decorator, however I don’t know this when the app is registering, since its unknown until the page is rendered

If you are looking for a generic callback that would work for some (or all) dcc.Graph components in the page, you can use a pattern matching callback with MATCH. So instead of define dcc.Graph(id=chart_id), you should use dcc.Graph(id={"type": "chart", "index": chart_id}), then the callback can use the pattern Input({"type": "chart", "index": MATCH}, "figure") (or Output/State, or other prop)…

The solution that you shared in the link will not work outside FigureWidgets (jupyter notebooks), so for Dash you need to do something slightly different. A good starting point is a callback like this:

@app.callback(
    Output({"type": "chart", "index": MATCH}, "figure"),
    Input({"type": "chart", "index": MATCH}, "relayoutData"),
    State({"type": "chart", "index": MATCH}, "figure"),
    prevent_initial_update=True,
)
def update_figure(relayout_data, fig):
    if (relayout_data is None) or ("xaxis.range[0]" not in relayout_data):
        raise PreventUpdate  # from dash.exceptions import PreventUpdate

    in_view = df.loc[relayout_data["xaxis.range[0]"] : relayout_data["xaxis.range[1]"]]

    # You must have layout_yaxis_autorange set to False
    fig["layout"]["yaxis"]["range"] = [
        in_view.min(),
        in_view.max(),
    ]

    return fig

Your rangeselector buttons will trigger a relayoutData update that in some cases will come with the new range for x axis (as well as when you zoom a portion of the timeseries). I use the xaxis range to figure out which points are “in view”, just like in @jmmease example. Knowing that, you can redefine the yaxis range and return the modified figure dict.

Note that 1- I haven’t added any extra spacing in the min/max, so you might want to adapt it a bit to your convenience, and 2- this is supposed to be a first working example of such functionality and the performance is bad (data is sent back-and-forth to the client, which is slow).

The way to solve point 2 is to write a clientside callback to do this update. Here is a much simplified version of such figure update on the javascript side.

1 Like