Update Data Points Using a clientside callback

How do I append new data points i.e. x axis and y axis values coming from input fields using a clientside_callback function. I am currently trying something like this but it is not updating the data in real time the updated data points show when I double click on the legend of the trace that is being updated ( due to the click the trace re-renders )

Code:

dash.register_page(__name__, path='/barchart-render')


# Page Layout for Different Filters and Dropdown Selections
def layout(**query_strings):
    layout = html.Div(
        [
            html.Div(
                children=[
                    html.H5('Label'),
                    dcc.Input(
                        id='Label',
                        style={'height': "50px", "width": "150px"}
                    ),
                    html.H5('Value'),
                    dcc.Input(
                        id='Value',
                        style={'height': "50px", "width": "150px"}
                    ),
                    dbc.Button(
                        id='High-button',
                        children="Update Chart",
                        n_clicks=0,
                        className='button'
                    )
                ]
            ),
            html.Div(
                children=[
                    dcc.Loading(
                        id="loading-graph",
                        children=[
                            html.Div(
                                dcc.Graph(id="barchart-graph-component", figure=BarChart),
                                className="graph",
                            )
                        ],
                        type="cube",
                        fullscreen=True,
                        color="#2a9d8f"
                    )
                ],
                className="main-graph-component card bg-secondary mb-3"
            )
        ],
        className="main-layout-div"
    )

    return layout

clientside_callback(
    """
    async function(n_clicks, label, value, figure) {
            console.log('n_clicks', n_clicks);
            if (n_clicks > 0 & label !== undefined & label !== 0 & value !== undefined & value !== 0 ) {
                value = parseInt(value);
                console.log(label, value);
                console.log('figure', figure);
                figure.data[0].x = [...figure.data[0].x, label];
                figure.data[0].y = [...figure.data[0].y, value];
                console.log('updated Figure', figure);
                return figure, 0;
            };
            return figure, 0;
            
    }
    """,
    Output('barchart-graph-component', 'figure'),
    Output('High-button', 'n_clicks'),
    Input('High-button', 'n_clicks'),
    State('Label', 'value'),
    State('Value', 'value'),
    State('barchart-graph-component', 'figure')
)

Hey,
use return JSON.parse(JSON.stringify((figure)), 0; to create a new object to update.

Not Working

Hello @ghulam_shabbir_khan,

Are you getting all of your console errors?

I think this would work:

dash.register_page(__name__, path='/barchart-render')


# Page Layout for Different Filters and Dropdown Selections
def layout(**query_strings):
    layout = html.Div(
        [
            html.Div(
                children=[
                    html.H5('Label'),
                    dcc.Input(
                        id='Label',
                        style={'height': "50px", "width": "150px"}
                    ),
                    html.H5('Value'),
                    dcc.Input(
                        id='Value',
                        style={'height': "50px", "width": "150px"}
                    ),
                    dbc.Button(
                        id='High-button',
                        children="Update Chart",
                        n_clicks=0,
                        className='button'
                    )
                ]
            ),
            html.Div(
                children=[
                    dcc.Loading(
                        id="loading-graph",
                        children=[
                            html.Div(
                                dcc.Graph(id="barchart-graph-component", figure=BarChart),
                                className="graph",
                            )
                        ],
                        type="cube",
                        fullscreen=True,
                        color="#2a9d8f"
                    )
                ],
                className="main-graph-component card bg-secondary mb-3"
            )
        ],
        className="main-layout-div"
    )

    return layout

clientside_callback(
    """
    async function(n_clicks, label, value, figure) {
            console.log('n_clicks', n_clicks);
            if (n_clicks && label && value) {
                value = parseInt(value);
                console.log(label, value);
                console.log('figure', figure);
                figure.data[0].x = [...figure.data[0].x, label];
                figure.data[0].y = [...figure.data[0].y, value];
                console.log('updated Figure', figure);
                return [JSON.parse(JSON.stringify((figure)), 0];
            };
            return [window.dash_clientside.no_update, 0];
            
    }
    """,
    Output('barchart-graph-component', 'figure'),
    Output('High-button', 'n_clicks'),
    Input('High-button', 'n_clicks'),
    State('Label', 'value'),
    State('Value', 'value'),
    State('barchart-graph-component', 'figure')
)
1 Like

I am getting an error in my console on trying your code:
TypeError: Cannot read properties of undefined (reading ‘apply’)

Without seeing more of your code, I dont think we can help much more.

Could you please create an MRE for use?

This is my current code keep in mind that the BarChart variable that I am loading from another file contains a plotly bar chart in dict format. Now the problem is that the clientside callback is returning the figure correctly but the figure is not being updated in real time. When I double click on my legend the updated figure shows up ( new bar is added). I am attaching my code and the state of the graph before and after double clicking the legend ( I am clicking the legend after the clientside callback is invoked )

Code:

from .barChartConfig import BarChart

# app = Dash(__name__)

dash.register_page(__name__, path='/barchart-render')


# Page Layout for Different Filters and Dropdown Selections
def layout(**query_strings):
    layout = html.Div(
        [
            html.Div(
                children=[
                    dcc.Input(value=BarChart, id="input",
                              type="text",
                              placeholder="",
                              style={'marginRight': '10px', 'display': 'none'}),
                    html.H5('Label'),
                    dcc.Input(
                        id='xaxis',
                        style={'height': "50px", "width": "150px"}
                    ),
                    html.H5('Value'),
                    dcc.Input(
                        id='yaxis',
                        style={'height': "50px", "width": "150px"}
                    ),
                    dbc.Button(
                        id='High-button',
                        children="Update Chart",
                        n_clicks=0,
                        className='button'
                    )
                ]
            ),
            html.Div(
                children=[
                    dcc.Loading(
                        id="loading-graph",
                        children=[
                            html.Div(
                                dcc.Graph(id="barchart-graph-component", figure=BarChart),
                                className="graph",
                            )
                        ],
                        type="cube",
                        fullscreen=True,
                        color="#2a9d8f"
                    )
                ],
                className="main-graph-component card bg-secondary mb-3"
            )
        ],
        className="main-layout-div"
    )

    return layout


clientside_callback(
    """
    function(n_clicks, xaxis_value, yaxis_value, figure) {
            console.log('n_clicks', JSON.parse(JSON.stringify(figure)));
            if ( n_clicks > 0 ) {
                console.log(xaxis_value, parseInt(yaxis_value));
                figure.data[0].x.push(xaxis_value);
                figure.data[0].y.push(yaxis_value);
                
                console.log('new figure', JSON.parse(JSON.stringify(figure)));
                
                return JSON.parse(JSON.stringify(figure)), 0;
            }
            return [window.dash_clientside.no_update, 0];

    }
    """,
    Output('barchart-graph-component', 'figure'),
    Output('High-button', 'n_clicks'),
    Input('High-button', 'n_clicks'),
    State('xaxis', 'value'),
    State('yaxis', 'value'),
    State('barchart-graph-component', 'figure'),
    prevent_intial_call=True
)

Before the double click on legend:

After The double click on legend the figure updates automatically:

Try this instead:

from .barChartConfig import BarChart

# app = Dash(__name__)

dash.register_page(__name__, path='/barchart-render')


# Page Layout for Different Filters and Dropdown Selections
def layout(**query_strings):
    layout = html.Div(
        [
            html.Div(
                children=[
                    dcc.Input(value=BarChart, id="input",
                              type="text",
                              placeholder="",
                              style={'marginRight': '10px', 'display': 'none'}),
                    html.H5('Label'),
                    dcc.Input(
                        id='xaxis',
                        style={'height': "50px", "width": "150px"}
                    ),
                    html.H5('Value'),
                    dcc.Input(
                        id='yaxis',
                        style={'height': "50px", "width": "150px"}
                    ),
                    dbc.Button(
                        id='High-button',
                        children="Update Chart",
                        n_clicks=0,
                        className='button'
                    )
                ]
            ),
            html.Div(
                children=[
                    dcc.Loading(
                        id="loading-graph",
                        children=[
                            html.Div(
                                dcc.Graph(id="barchart-graph-component", figure=BarChart),
                                className="graph",
                            )
                        ],
                        type="cube",
                        fullscreen=True,
                        color="#2a9d8f"
                    )
                ],
                className="main-graph-component card bg-secondary mb-3"
            )
        ],
        className="main-layout-div"
    )

    return layout


clientside_callback(
    """
    function(n_clicks, xaxis_value, yaxis_value, figure) {
            console.log('n_clicks', JSON.parse(JSON.stringify(figure)));
            if ( n_clicks > 0 ) {
                newFigure = JSON.parse(JSON.stringify(figure))
                console.log(xaxis_value, parseInt(yaxis_value));
                newFigure.data[0].x.push(xaxis_value);
                newFigure.data[0].y.push(yaxis_value);
               
                
                
                return newFigure, 0;
            }
            return [window.dash_clientside.no_update, 0];

    }
    """,
    Output('barchart-graph-component', 'figure'),
    Output('High-button', 'n_clicks'),
    Input('High-button', 'n_clicks'),
    State('xaxis', 'value'),
    State('yaxis', 'value'),
    State('barchart-graph-component', 'figure'),
    prevent_intial_call=True
)

I think manipulating the figure object is done in place, thus it is essentially being updated without triggering to rerender. Thus, making the difference before we start modifying, this should update now.

Still not working, and after trying your code the re render on double clicking the legend stopped working, dk why. I am thinking maybe it is because of introducing a new figure variable, but it should work still.

from .barChartConfig import BarChart

# app = Dash(__name__)

dash.register_page(__name__, path='/barchart-render')


# Page Layout for Different Filters and Dropdown Selections
def layout(**query_strings):
    layout = html.Div(
        [
            html.Div(
                children=[
                    dcc.Input(value=BarChart, id="input",
                              type="text",
                              placeholder="",
                              style={'marginRight': '10px', 'display': 'none'}),
                    html.H5('Label'),
                    dcc.Input(
                        id='xaxis',
                        style={'height': "50px", "width": "150px"}
                    ),
                    html.H5('Value'),
                    dcc.Input(
                        id='yaxis',
                        style={'height': "50px", "width": "150px"}
                    ),
                    dbc.Button(
                        id='High-button',
                        children="Update Chart",
                        n_clicks=0,
                        className='button'
                    )
                ]
            ),
            html.Div(
                children=[
                    dcc.Loading(
                        id="loading-graph",
                        children=[
                            html.Div(
                                dcc.Graph(id="barchart-graph-component", figure=BarChart),
                                className="graph",
                            )
                        ],
                        type="cube",
                        fullscreen=True,
                        color="#2a9d8f"
                    )
                ],
                className="main-graph-component card bg-secondary mb-3"
            )
        ],
        className="main-layout-div"
    )

    return layout


clientside_callback(
    """
    function(n_clicks, xaxis_value, yaxis_value, figure) {
            console.log('n_clicks', JSON.parse(JSON.stringify(figure)));
            if ( n_clicks > 0 ) {
                newFigure = JSON.parse(JSON.stringify(figure))
                console.log(xaxis_value, parseInt(yaxis_value));
                newFigure.data[0].x.push(xaxis_value);
                newFigure.data[0].y.push(yaxis_value);
               
                
                
                return [newFigure, 0];
            }
            return [window.dash_clientside.no_update, 0];

    }
    """,
    Output('barchart-graph-component', 'figure'),
    Output('High-button', 'n_clicks'),
    Input('High-button', 'n_clicks'),
    State('xaxis', 'value'),
    State('yaxis', 'value'),
    State('barchart-graph-component', 'figure'),
    prevent_intial_call=True
)

What about this?

This worked!!! :sweat_smile: I am so thankful for you help. :smiling_face_with_three_hearts:

1 Like

You’re welcome!

JS is not as forgiving with how the outputs are, if you have multiple, they need to be wrapped as a list. :slight_smile:

1 Like

I didn’t know that until now :laughing:. Thanks for the knowledge!

1 Like