Dynamic layout does not propagate resized graph dimensions until window is resized

And this particular callback, without @, is in the last bunch of dash apps examples available in the dash gallery.

app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="resize"),
    Output("output-clientside", "children"),
    [Input("count_graph", "figure")],
)

So, when Input is updated, the resize function is executed.

Trying to figure out how to force window.dispatchEvent(new Event(‘resize’) to be triggered when i change a className. Will update this post once done

EDIT

Ok, done.

Here is how to proceed:

  1. app.py must import:
    from dash.dependencies import Input, Output, State, ClientsideFunction

  2. let’s include the below Div somewhere in the Dash layout:
    html.Div(id="output-clientside"),

  3. asset folder must include either your own script, or the default script resizing_script.js, which contains:

    if (!window.dash_clientside) {
        window.dash_clientside = {};
    }
    window.dash_clientside.clientside = {
        resize: function(value) {
            console.log("resizing..."); // for testing
            setTimeout(function() {
                window.dispatchEvent(new Event("resize"));
                console.log("fired resize");
            }, 500);
        return null;
        },
    };
    
  4. Among your callbacks, put this one, without @:

    app.clientside_callback(
        ClientsideFunction(namespace="clientside", function_name="resize"),
        Output("output-clientside", "children"),
        [Input("yourGraph_ID", "figure")],
    )    
    

At this point, when you manually resize the window, in your browser, the resize function is triggered.

We aim to achieve the same result, but without manual window resizing. For instance, the trigger could be a className update.

So, we apply the following changes:
Step 1: unchanged
Step 2: unchanged
Step 3: let’s add a “resize2” function inside our javascript file, which takes 2 arguments:

if (!window.dash_clientside) {
  window.dash_clientside = {};
}
window.dash_clientside.clientside = {
  resize: function(value) {
    console.log("resizing..."); // for testing
    setTimeout(function() {
      window.dispatchEvent(new Event("resize"));
      console.log("fired resize");
    }, 500);
    return null;
  },

  resize2: function(value1, value2) {
    console.log("resizingV2..."); // for testing
    setTimeout(function() {
       window.dispatchEvent(new Event("resize"));
       console.log("fired resizeV2");
    }, 500);
    return value2; // for testing
  }
};

Function “resize2” now takes 2 arguments, one for each Input defined in the below callback. It will return the value of “value2” in the Output, specified in this very same callback. You can set it back to “null”, it’s just to illustrate.

Step4: our callback now becomes:

app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="resize2"),
    Output("output-clientside", "children"),
    [Input("yourGraph_ID", "figure"), Input("yourDivContainingYourGraph_ID", "className")],
)    

Finally, you need a button to trigger the event which will change the className of your container.

let’s say your have:

daq.ToggleSwitch(
    id='switchClassName',
    label={
        'label':['Option1', 'Option2'],
    },          
    value=False,                                          
),  

And the following callback:

@app.callback(Output("yourDivContainingYourGraph_ID", "className"), 
              [Input("switchClassName","value")]
              )
def updateClassName(value):
    if value==False:
        return "twelve columns"
    else:
        return "nine columns"

And now, if you save everything, refresh, everytime you press on your toggleSwitch,it resizes the container, triggers the function, and refresh the figure.

Given the way it’s done, I assume it must also be possible to run more Javascript functions, the same way.

Hope it will help some :slight_smile:

3 Likes