Clientside Callback Bug

Hello Plotly Community,

I hope this message finds you well. I’m currently developing a Dash application and have run into an issue with client-side callbacks that I could use some help with.

Application Overview

My application consists of two main components:

  1. Category Buttons: I have a set of buttons representing different medical categories (e.g., ‘Cardiology’ and ‘Neurology’). When a category button is clicked, the application should filter and display a list of doctors associated with that category.
  2. Doctor Buttons: After selecting a category, corresponding buttons for doctors should appear, allowing users to select a doctor for more details.
from dash import Dash, html,  Input, Output, ALL, clientside_callback

app = Dash(__name__)

app.layout = html.Div([
    html.Div([html.Button(c, id={"type": "category", "index": c} ) for c in ['Cardiology', 'Neurology']]),
    html.Div(id='output-list'),
    html.Div(id='doctor-detail')

])

# CATEGORY CALLBACK
clientside_callback(
    """function maximize_chart(n_clicks) {
        const no_update = window.dash_clientside.no_update
        let ctx = window.dash_clientside.callback_context;

    if(ctx.triggered.length===0){
        return no_update
    }

    function button(props){
                                                                                                                                        
    return {
        'props': props, 
        'type': 'Button',
         'namespace': 'dash_html_components'
        }
    }

    doctors = [
        {'id':1, 'category':'Cardiology','name':'John Doe'},
        {'id':2, 'category':'Cardiology','name':'Tom Jink'},
        {'id':3, 'category':'Neurology', 'name':'Sara Doen'},
        {'id':4, 'category':'Neurology', 'name':'Tim Jaak'}
    ]

    let category = JSON.parse(ctx.triggered[0]['prop_id'].split('.')[0]).index

    doctors = doctors.filter( item =>  item.category === category)

    const doctorCards = doctors.map(doctor =>
    button({
        'id' : {"type": "doctor", "index": doctor.id},
        'children':doctor.name
        })
    );

    return doctorCards

    }
    """,
    Output('output-list', 'children'),
    Input({"type": "category", "index": ALL}, "n_clicks"),
     prevent_intial_call = True
 
)

# DOCTOR CALLBACK
clientside_callback(
    """function maximize_chart(n_clicks) {
        const no_update = window.dash_clientside.no_update
        let ctx = window.dash_clientside.callback_context;
        console.log('DOCTOR CALLBACK TRIGGERED',ctx.triggered)

    return no_update

    }
    """,
    Output('doctor-detail', 'children'),
    Input({"type": "doctor", "index": ALL}, "n_clicks"),
     prevent_intial_call = True
 
)

if __name__ == '__main__':
    app.run_server(debug=True)

Issue

When I click on a category button, the doctor callback is unexpectedly triggered. This behavior seems to occur even though the doctor callback should only activate upon clicking the doctor buttons generated after selecting a category.

Debugging Steps

• I’ve added console.log statements to the doctor callback to track when it is triggered and to understand the context of the clicks.
• I ensured that the doctor buttons are only created after a category is selected, but the callback still seems to trigger without clicking on any doctor buttons.

I would appreciate any insights or suggestions on how to resolve this issue. How can I ensure that the doctor callback only responds to actual clicks on doctor buttons and not when clicking category buttons?

Thank you for your time and help!

Hello @Amazigh,

This is an issue with pattern-matching callbacks.

There has been much debate over how to overcome things like this in the application, especially with issues such as what we encounter.

However, a newer workaround I just thought of is the dash_clientside.set_props which allows you to use a MATCH callback and target a non-matching output. This might work well for your case. Especially with the clientside callback, as it triggers directly from JS.

1 Like

Thank you @jinnyzor for the fast reply. Do you have a code snippet or an example of how the set props is being used ?

Sure, you can check here for how it works in Python:

And here is how it works on the clientside: