Skip to content

Adding Pattern Matching selector ALLELSE #3206

Open
@celia-lm

Description

@celia-lm

Similar to ALLSMALLER but doesn't asume that id index are numeric and sequential

Use case

General description

  • There's a set of components with the same pattern-matching id type.
  • Based on an interaction with one of those components (or with another component with the same pattern-matching id index), something about it will change.
  • That same property will also change in the rest of the components of the set, but with a different value.

Example

  • We have 6 clickable cards.
  • Each clickable card is an html.Div with pattern-matching id {'type':'card,'index': f"card_{i}"} wrapped in an html.A with pattern-matching id {'type':'invisible_button,'index': f"card_{i}"}.
  • When a user clicks one card, its style changes to HIGHLIGHT_STYLE (red) and the style of the rest changes to DEFAULT_STYLE (black).
Screen.Recording.2025-03-10.at.18.10.46.mov

Desired behavior/code

@callback(
    Output({'type':'card','id': MATCH}, 'style'),
    Output({'type':'card','id': ALLELSE}, 'style'),
    Input({'type':'invisible_button','id': MATCH}, 'n_clicks'),
    )
def restore_card_format(n_clicks):
    return HIGHLIGHT_STYLE, [DEFAULT_STYLE]*5

Current behavior/code

@callback(
    Output({'type':'card','id': ALL}, 'style'),
    Input({'type':'invisible_button','id': ALL}, 'n_clicks'),
    )
def restore_card_format(n_clicks):
    return DEFAULT_STYLE

@callback(
    Output({'type':'card','id': MATCH}, 'style'),
    Input({'type':'invisible_button','id': MATCH}, 'n_clicks'),
    )
def highlight_card(n_clicks):
    return HIGHLIGHT_STYLE

Using the above code produces an error like this:

In the callback for output(s): {"id":MATCH,"type":"card"}.style Output 0 ({"id":MATCH,"type":"card"}.style) overlaps another output ({"id":ALL,"type":"card"}.style) used in a different callback.

Workaround - current way to implement this functionality:

from dash import Dash, Input, Output, callback, html, dcc, ALL, MATCH, ctx

app = Dash()
server = app.server

DEFAULT_STYLE = {
    "height":"200px",
    "width":"200px",
    "margin":"5px",
    "background-color": "black",
    "color":"white"
    }

HIGHLIGHT_STYLE = {
    "height":"200px",
    "width":"200px",
    "margin":"5px",
    "background-color": "red",
    }

app.layout = html.Div([
    html.A(
        html.Div(
            id={'type':'card','index': f"card_{i}"}, 
            style=DEFAULT_STYLE,
            children=f"card_{i}"
            ),
        id={'type':'invisible_button','index': f"card_{i}"}, 
        )
    for i in range(6)
], style={"display":"inline-flex"})

@callback(
    Output({'type':'card','index': ALL}, 'style'),
    Input({'type':'invisible_button','index': ALL}, 'n_clicks'),
    prevent_initial_call=True
    )
def highlight_card(n_clicks):
    selected_card_id = ctx.triggered_id["index"]
    # this is how ctx.outputs_list looks like
    # [{'id': {'index': 'card_0', 'type': 'card'}, 'property': 'style'}, {'id': {'index': 'card_1', 'type': 'card'}, ...]
    new_styles = [HIGHLIGHT_STYLE if card["id"]["index"]==selected_card_id else DEFAULT_STYLE for card in ctx.outputs_list]
    return new_styles

app.run(debug=True)

Metadata

Metadata

Labels

P1needed for current cyclecscustomer successfeaturesomething new

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions