[Python] Dash pattern-matching callback issue

Discussão em 'Python' iniciado por Stack, Dezembro 21, 2024.

  1. Stack

    Stack Membro Participativo

    I am working on a dash app where that will have a dash bootstrap component AccordionItems dynamically created.

    When I click the search-trigger button, the dialog box is opened and a query to a database is made to retrieve the categories accordion items (first callback). The categories AccordionItems children are html.Divs with a dictionary id which will be used for the MATCH pattern-matching callback. The second callback is able to create the products accordion and its items. However, it is being triggered several times when I expected it to be triggered only once (for the active_item) as it is possible to see with the print(active_item). In the example code below it runs quickly, as the number of categories and products are reduced but it becomes an issue when there are lots of options, which is my case.

    I don't really see why this second callback is being triggered more than once and haven't found any way to avoid this and still get the products accordion created.

    import dash
    import dash_bootstrap_components as dbc
    from dash import Input, Output, html, ctx, MATCH

    app = dash.Dash(__name__, external_stylesheets=[dbc.themes.DARKLY])

    app.layout = dbc.Container([
    dbc.Row(children=[
    dbc.Col(children=[
    html.Div([
    # Search box button
    html.Div([
    dbc.Button(
    children=[
    "Search products..."
    ],
    id="search-trigger",
    n_clicks=0
    ),
    ]),

    # Dialog box
    dbc.Modal(
    [
    html.Div(
    dbc.Accordion(id='categories'),
    style={'max-height': '70vh', 'overflow-y': 'auto'}
    ),

    dbc.ModalFooter(
    dbc.Button("Close", id="close-modal", className="ms-auto", n_clicks=0)
    ),
    ],
    id="search-modal",
    is_open=False,
    )
    ])
    ])
    ])
    ])

    # Callback to open search product dialog box
    @app.callback(
    [Output("search-modal", "is_open"),
    Output("categories", "children"),],
    [Input("search-trigger", "n_clicks"),
    Input("close-modal", "n_clicks")],
    prevent_initial_call=True
    )
    def toggle_modal(search_click, close_click):

    triggered_id = ctx.triggered_id
    if triggered_id == "search-trigger":

    # example
    # originally this is a query to a database to fetch data
    categories = [
    ("Technology", 1),
    ("Health", 2),
    ("Finance", 3),
    ("Education", 4),
    ("Travel", 5),
    ("Entertainment", 6),
    ("Sports", 7),
    ("Lifestyle", 8),
    ("Food", 9),
    ("Science", 10)
    ]

    children = [
    dbc.AccordionItem(
    title=category[0],
    item_id=category[1],
    children=html.Div(id={"type": "products-by-category", "index": category[1]})
    ) for category in categories
    ]

    return True, children

    elif triggered_id == "close-modal":
    return False, []

    return False, []

    @app.callback(
    Output({"type": "products-by-category", "index": MATCH}, "children"),
    [Input("categories", "active_item")],
    prevent_initial_call=True
    )
    def load_subitems(active_item):

    if active_item:

    print(active_item)

    # originally this is a query that will be made based on the active_item
    # results = query(active_item)
    products = {
    1: ["Laptop", "Smartphone", "Tablet"],
    2: ["Vitamins", "Fitness Tracker", "Yoga Mat"],
    3: ["Stocks App", "Budget Planner", "Cryptocurrency Wallet"],
    4: ["Online Course", "Textbook", "Notebook"],
    5: ["Suitcase", "Travel Pillow", "Guidebook"],
    6: ["Streaming Subscription", "Board Game", "Concert Ticket"],
    7: ["Soccer Ball", "Running Shoes", "Fitness Band"],
    8: ["Smartwatch", "Home Decor", "Fashion Accessories"],
    9: ["Cookbook", "Blender", "Gourmet Ingredients"],
    10: ["Telescope", "Microscope", "Lab Kit"]
    }

    selected_category = products[active_item]

    children = [
    dbc.Accordion(
    [dbc.AccordionItem(
    title=products
    ) for products in selected_category],
    id='products',
    start_collapsed=True
    )

    ]
    return children

    # If not the active item, return an empty list
    return []

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

    Continue reading...

Compartilhe esta Página