# Pattern-Matching Callbacks

> To get the most out of this page, make sure you've read about Basic Callbacks in the Dash Fundamentals.

The pattern-matching callback selectors `MATCH`, `ALL`, & `ALLSMALLER` allow you to write callbacks that respond to or update an arbitrary or dynamic number of components.

## Simple Example with `ALL`

> This example uses partial property updates, introduced in Dash 2.9. For an example that works with earlier versions of Dash, see Simple `ALL` Example Without Partial Updates at the end of this example.

This example renders an arbitrary number of `dcc.Dropdown` elements and the callback is fired whenever any of the `dcc.Dropdown` elements change. Try adding a few dropdowns and selecting their values to see how the app updates.

    # Simple Example with ALL

    library(dash)
    library(dashCoreComponents)
    library(dashHtmlComponents)

    app <- Dash$new()

    app$layout(htmlDiv(list(
      htmlButton("Add Filter", id="add-filter", n_clicks=0),
      htmlDiv(id="dropdown-container", children=list()),
      htmlDiv(id="dropdown-container-output")
    )))

    app$callback(
      output(id="dropdown-container", property = "children"),
      params = list(
        input(id = "add-filter", property = "n_clicks"),
        state(id = "dropdown-container", property = "children")
      ),
      display_dropdowns <- function(n_clicks, children){
        new_dropdown = dccDropdown(
          id=list(
            "index" = n_clicks,
            "type" = "filter-dropdown"
          ),
          options = lapply(c("NYC", "MTL", "LA", "TOKYO"), function(x){
            list("label" = x, "value" = x)
          })
        )
        children[[n_clicks + 1]] <- new_dropdown
        return(children)
      }
    )

    app$callback(
      output(id="dropdown-container-output", property="children"),
      params = list(
        input(id=list("index" = ALL, "type" = "filter-dropdown"), property= "value")
      ),
      display_output <- function(test){
        ctx <- app$callback_context()
        return(htmlDiv(
          lapply(seq_along(test), function(x){
            return(htmlDiv(sprintf("Dropdown %s = %s", x, test[[x]])))
          })
        ))
      }
    )

    app$run_server()

Select...
Dropdown 1 = None

Some notes about this example:

- Notice how the `id` in `dcc.Dropdown` is a dictionary rather than a string. This is a new feature that we enabled for pattern-matching callbacks (previously, IDs had to be strings).
- In our second callback, we have `Input({'type': 'city-filter-dropdown', 'index': ALL}, 'value')`. This means "match any input that has an ID dictionary where `'type'` is `'city-filter-dropdown'` and `'index'` is anything. Whenever the `value` property of any of the dropdowns change, send all of their values to the callback."
- The keys & values of the ID dictionary (`type`, `index`, `city-filter-dropdown`) are arbitrary. This could've be named `{'foo': 'bar', 'baz': n_clicks}`.
- However, for readability, we recommend using keys like `type`, `index`, or `id`. `type` can be used to refer to the class or set dynamic components and `index` or `id` could be used to refer which component you are matching within that set. In this example, we just have a single set of dynamic components but you may have multiple sets of dynamic components in more complex apps or if you are using `MATCH` (see below).
- In fact, in this example, we didn't actually need `'type': 'city-filter-dropdown'`. The same callback would have worked with `Input({'index': ALL}, 'value')`. We included `'type': 'city-filter-dropdown'` as an extra specifier in case you create multiple sets of dynamic components.
- The component properties themselves (e.g. `value`) cannot be matched by a pattern, only the IDs are dynamic.
- This example uses `Patch` to make a partial update to the `'children'` property of `'dropdown-container-div'`. We append a dropdown each time the first callback runs.

Simple `ALL` Example Without Partial Updates

This example is similar to the Simple Example with `ALL` above, but uses `State` to access the list of currently displayed dropdowns. In the first callback, we then append to this list of dropdowns and return it as the output.

> This example has not been ported to R yet - showing the Python version instead.

> Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21

    from dash import Dash, dcc, html, Input, Output, State, ALL, callback

    app = Dash(__name__, suppress_callback_exceptions=True)

    app.layout = html.Div([
        html.Button("Add Filter", id="add-filter", n_clicks=0),
        html.Div(id='dropdown-container', children=[]),
        html.Div(id='dropdown-container-output')
    ])

    @callback(
        Output('dropdown-container', 'children'),
        Input('add-filter', 'n_clicks'),
        State('dropdown-container', 'children'))
    def display_dropdowns(n_clicks, children):
        new_dropdown = dcc.Dropdown(
            ['NYC', 'MTL', 'LA', 'TOKYO'],
            id={
                'type': 'filter-dropdown',
                'index': n_clicks
            }
        )
        children.append(new_dropdown)
        return children

    @callback(
        Output('dropdown-container-output', 'children'),
        Input({'type': 'filter-dropdown', 'index': ALL}, 'value')
    )
    def display_output(values):
        return html.Div([
            html.Div('Dropdown {} = {}'.format(i + 1, value))
            for (i, value) in enumerate(values)
        ])

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

## Simple Example with `MATCH`

> This example uses partial property updates, introduced in Dash 2.9. For an example that works with earlier versions of Dash, see Simple `MATCH` Example Without Partial Updates at the end of this example.

Like `ALL`, `MATCH` will fire the callback when any of the component's properties change. However, instead of passing all of the values into the callback, `MATCH` will pass just a single value into the callback. Instead of updating a single output, it will update the dynamic output that is "matched" with.

    # Simple Example with MATCH

    library(dash)
    library(dashCoreComponents)
    library(dashHtmlComponents)

    app <- Dash$new()

    app$layout(htmlDiv(list(
      htmlButton("Add Filter", id="dynamic-add-filter", n_clicks=0),
      htmlDiv(id="dynamic-dropdown-container", children = list())
    )))

    app$callback(
      output(id="dynamic-dropdown-container", "children"),
      params = list(
        input("dynamic-add-filter", "n_clicks"),
        state("dynamic-dropdown-container", "children")
      ),
      display_dropdown <- function(n_clicks, children){
        new_element = htmlDiv(list(
          dccDropdown(
            id = list("index" = n_clicks, "type" = "dynamic-dropdown"),
            options = lapply(c("NYC", "MTL", "LA", "TOKYO"), function(x){
              list("label" = x, "value" = x)
            })
          ),
          htmlDiv(
            id = list("index" = n_clicks, "type" = "dynamic-output"),
            children = list()
          )
        ))

        children <- c(children, list(new_element))
        return(children)
      }
    )

    app$callback(
      output(id = list("index" = MATCH, "type" = "dynamic-output"), property= "children"),
      params = list(
        input(id=list("index" = MATCH, "type" = "dynamic-dropdown"), property= "value"),
        state(id=list("index" = MATCH, "type" = "dynamic-dropdown"), property= "id")
      ),
      display_output <- function(value, id){
        return(htmlDiv(sprintf("Dropdown %s = %s", id$index, value)))
      }
    )

    app$run_server()

Select...
Dropdown 0 = None

Notes about this example:

- The `display_dropdowns` callback returns two elements with the same `index`: a dropdown and a div.

- The second callback uses the `MATCH` selector. With this selector, we're asking Dash to:

  1. Fire the callback whenever the `value` property of any component with the id `'type': 'dynamic-dropdown'` changes: `Input({'type': 'dynamic-dropdown', 'index': MATCH}, 'value')`
  2. Update the component with the id `'type': 'dynamic-output'` and the `index` that matches the same `index` of the input: `Output({'type': 'dynamic-output', 'index': MATCH}, 'children')`
  3. Pass along the `id` of the dropdown into the callback: `State({'type': 'dynamic-dropdown', 'index': MATCH}, 'id')`

- With the `MATCH` selector, only a single value is passed into the callback for each `Input` or `State`. This is unlike the previous example with the `ALL` selector where Dash passed all of the values into the callback.

- Notice how it's important to design IDs dictionaries that "line up" the inputs with outputs. The `MATCH` contract is that Dash will update whichever output has the same dynamic ID as the id. In this case, the "dynamic ID" is the value of the `index` and we've designed our layout to return dropdowns & divs with identical values of `index`.
- In some cases, it may be important to know which dynamic component changed. As above, you can access this by setting `id` as `State` in the callback.

- You can also use `dash.callback_context` to access the inputs and state and to know which input changed. `outputs_list` is particularly useful with `MATCH` because it can tell you which dynamic component this particular invocation of the callback is responsible for updating. Here is what that data might look like with two dropdowns rendered on the page after we change the first dropdown.

  - `dash.callback_context.triggered_prop_ids` (available from Dash 2.4) returns a dictionary of inputs that triggered the callback. Each key is a `<component_id>.<component_property>` and the corresponding value is the `<component_id>`. In this example, we can see that the id of the component that triggered the callback was `{'index': 0, 'type': 'dynamic-dropdown'}`and the property was `value`:

        {
          '{"index":0,"type":"dynamic-dropdown"}.value': {
              "index": 0,
              "type": "dynamic-dropdown",
          }
        }

  - `dash.callback_context.triggered`. Note that the `prop_id` is a stringified dictionary with no whitespace.

- `dash.callback_context.inputs`. Note that the key is a stringified dictionary with no whitespace.

- `dash.callback_context.inputs_list`. Each element of the list corresponds to one of the input declarations. If one of the input declarations matches a pattern then it will contain a list of values.

- `dash.callback_context.outputs_list`

Simple `MATCH` Example Without Partial Updates

> This example has not been ported to R yet - showing the Python version instead.

> Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21

    from dash import Dash, dcc, html, Input, Output, State, MATCH, callback

    app = Dash(__name__, suppress_callback_exceptions=True)

    app.layout = html.Div([
        html.Button('Add Filter', id='dynamic-add-filter', n_clicks=0),
        html.Div(id='dynamic-dropdown-container', children=[]),
    ])

    @callback(
        Output('dynamic-dropdown-container', 'children'),
        Input('dynamic-add-filter', 'n_clicks'),
        State('dynamic-dropdown-container', 'children'))
    def display_dropdowns(n_clicks, children):
        new_element = html.Div([
            dcc.Dropdown(
                ['NYC', 'MTL', 'LA', 'TOKYO'],
                id={
                    'type': 'dynamic-dropdown',
                    'index': n_clicks
                }
            ),
            html.Div(
                id={
                    'type': 'dynamic-output',
                    'index': n_clicks
                }
            )
        ])
        children.append(new_element)
        return children

    @callback(
        Output({'type': 'dynamic-output', 'index': MATCH}, 'children'),
        Input({'type': 'dynamic-dropdown', 'index': MATCH}, 'value'),
        State({'type': 'dynamic-dropdown', 'index': MATCH}, 'id'),
    )
    def display_output(value, id):
        return html.Div('Dropdown {} = {}'.format(id['index'], value))

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

## Simple Example with `ALLSMALLER`

> This example uses partial property updates, introduced in Dash 2.9. For an example that works with earlier versions of Dash, see Simple `ALLSMALLER` Example Without Partial Updates at the end of this example.

In the following example, `ALLSMALLER` is used to pass in the values of all of the dropdowns on the page that have an index smaller than the index corresponding to the div.

The user interface in the example below displays filter results that are increasingly specific in each as we apply each additional dropdown.

`ALLSMALLER` can only be used in `Input` and `State` items, and must be used on a key that has `MATCH` in the `Output` item(s).

`ALLSMALLER` it isn't always necessary (you can usually use `ALL` and filter out the indices in your callback) but it will make your logic simpler.

    # Example with ALLSMALLER

    library(dash)
    library(dashCoreComponents)
    library(dashHtmlComponents)

    df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv', stringsAsFactors = FALSE)

    app <- Dash$new()

    app$layout(htmlDiv(list(
      htmlButton("Add Filter", id = "add-filter-ex3", n_clicks = 0),
      htmlDiv(id = "container-ex3", children = list())
    )))

    app$callback(
      output('container-ex3', 'children'),
      params = list(
        input('add-filter-ex3', 'n_clicks'),
        state('container-ex3', 'children')
      ),
      display_dropdowns <- function(n_clicks, existing_children){
        new_children <- htmlDiv(list(
          dccDropdown(
            id = list("index" = n_clicks, "type" = "filter-dropdown-ex3"),
            options = lapply(unique(df$country), function(x){
              list("label" = x, "value" = x)
            }),
            value = unique(df$country)[n_clicks + 1]
          ),
          htmlDiv(id = list("index" = n_clicks, "type" = "output-ex3"), children = list(unique(df$country)[n_clicks + 1]))
        ))

        existing_children <- c(existing_children, list(new_children))
      }
    )

    app$callback(
      output(id = list("type" = "output-ex3", "index" = MATCH), property = "children"),
      params = list(
        input(id = list("type" = "filter-dropdown-ex3", "index" = MATCH), property = "value"),
        input(id = list("type" = "filter-dropdown-ex3", "index" = ALLSMALLER), property = "value")
      ),
      display_output <- function(matching_value, previous_values){
        previous_values_in_reversed_order = rev(previous_values)
        all_values = c(matching_value, previous_values_in_reversed_order)
        all_values = unlist(all_values)

        dff = df[df$country %in% all_values,]
        avgLifeExp = round(mean(dff$lifeExp), digits = 2)

        if (length(all_values) == 1) {
          return(
            htmlDiv(sprintf("%s is the life expectancy of %s.", avgLifeExp, matching_value))
          )
        } else if (length(all_values) == 2) {
          return(
            htmlDiv(sprintf("%s is the life expectancy of %s.", avgLifeExp, paste(all_values, collapse = " and ")))
          )
        } else {
          return(
            htmlDiv(sprintf("%s is the life expectancy of %s, and %s.", avgLifeExp, paste(all_values[-length(all_values)], collapse = " , "), paste(all_values[length(all_values)])))
          )
        }
      }
    )

    app$run_server()

Afghanistan
×
43.83 is the life expectancy of Afghanistan

- In the example above, try adding a few filters and then change the first dropdown. Notice how changing this dropdown will update the text of each `html.Div` that has an index that depends on that dropdown.
- That is, each `html.Div` will get updated whenever any of the dropdowns with an `index` smaller than it has changed.
- So, if there are 10 filters added and the first dropdown has changed, Dash will fire your callback 10 times, once to update each `html.Div` that depends on the `dcc.Dropdown` that changed.

- As above, you can also use `dash.callback_context` to access the inputs and state and to know which input changed. Here is what that data might look like when updating the second div with two dropdowns rendered on the page after we change the first dropdown.

  - `dash.callback_context.triggered_prop_ids` (available from Dash 2.4) returns a dictionary of inputs that triggered the callback. Each key is a `<component_id>.<component_property>` and the corresponding value is the `<component_id>`. In this example, we can see that the id of the component that triggered the callback was `{'index': 0, 'type': 'filter-dropdown-ex3'}`and the property was `value`:

        {
          '{"index":0,"type":"filter-dropdown-ex3"}.value': {
              "index": 0,
              "type": "filter-dropdown-ex3",
          }
        }

  - `dash.callback_context.triggered`. Note that the `prop_id` is a stringified dictionary with no whitespace.

  - `dash.callback_context.inputs`. Note that the key is a stringified dictionary with no whitespace.

  - `dash.callback_context.inputs_list`. Each element of the list corresponds to one of the input declarations. If one of the input declarations matches a pattern then it will contain a list of values.

  - `dash.callback_context.outputs_list`

`ALLSMALLER` Example Without Partial Updates

> This example has not been ported to R yet - showing the Python version instead.

> Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21

    from dash import Dash, dcc, html, Input, Output, State, MATCH, ALLSMALLER, callback
    import pandas as pd

    df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')

    app = Dash(__name__, suppress_callback_exceptions=True)

    app.layout = html.Div([
        html.Button('Add Filter', id='add-filter-ex3', n_clicks=0),
        html.Div(id='container-ex3', children=[]),
    ])

    @callback(
        Output('container-ex3', 'children'),
        Input('add-filter-ex3', 'n_clicks'),
        State('container-ex3', 'children'))
    def display_dropdowns(n_clicks, existing_children):
        existing_children.append(html.Div([
            dcc.Dropdown(
                df['country'].unique(),
                df['country'].unique()[n_clicks],
                id={
                    'type': 'filter-dropdown-ex3',
                    'index': n_clicks
                }
            ),
            html.Div(id={
                'type': 'output-ex3',
                'index': n_clicks
            })
        ]))
        return existing_children

    @callback(
        Output({'type': 'output-ex3', 'index': MATCH}, 'children'),
        Input({'type': 'filter-dropdown-ex3', 'index': MATCH}, 'value'),
        Input({'type': 'filter-dropdown-ex3', 'index': ALLSMALLER}, 'value'),
    )
    def display_output(matching_value, previous_values):
        previous_values_in_reversed_order = previous_values[::-1]
        all_values = [matching_value] + previous_values_in_reversed_order

        dff = df[df['country'].str.contains('|'.join(all_values))]
        avgLifeExp = dff['lifeExp'].mean()

        # Return a slightly different string depending on number of values
        if len(all_values) == 1:
            return html.Div('{:.2f} is the life expectancy of {}'.format(
                avgLifeExp, matching_value
            ))
        elif len(all_values) == 2:
            return html.Div('{:.2f} is the average life expectancy of {}'.format(
                avgLifeExp, ' and '.join(all_values)
            ))
        else:
            return html.Div('{:.2f} is the average life expectancy of {}, and {}'.format(
                avgLifeExp, ', '.join(all_values[:-1]), all_values[-1]
            ))

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

## Todo App

Creating a Todo App is a classic UI exercise in that demonstrates many features in common "create, read, update and delete" (CRUD) applications.

> This example uses partial property updates and duplicate callback outputs, introduced in Dash 2.9. For an example that works with earlier versions of Dash, see Todo App Without Partial Updates below.

    # TO DO Sample App

    library(dash)
    library(dashCoreComponents)
    library(dashHtmlComponents)

    app <- Dash$new()

    app$layout(htmlDiv(list(
      htmlDiv('Dash To-Do List'),
      dccInput(id = 'new-item'),
      htmlButton("Add", id = "add"),
      htmlButton("Clear Done", id = "clear-done"),
      htmlDiv(id = "list-container"),
      htmlDiv(id = "totals")
    )))

    style_todo <- list("display" = "inline", "margin" = "10px")
    style_done <- list("display" = "inline", "margin" = "10px", "textDecoration" = "line-through", "color" = "#888")

    app$callback(
      output = list(
        output("list-container", "children"),
        output("new-item", "value")
      ),
      params = list(
        input("add", "n_clicks"),
        input("new-item", "n_submit"),
        input("clear-done", "n_clicks"),
        state("new-item", "value"),
        state(list("index" = ALL, "type" = "check"), "children"),
        state(list("index" = ALL, "type" = "done"), "value")
      ),
      edit_list <- function(add, add2, clear, new_item, items, items_done) {
        ctx <- app$callback_context()
        triggered <- ifelse(is.null(ctx$triggered$prop_id), " ", ctx$triggered$prop_id)
        adding <- grepl(triggered, "add.n_clicks|new-item.n_submit")
        clearing = grepl(triggered, "clear-done.n_clicks")

        # Create a list which we will hydrate with "items" and "items_done"
        new_spec <- list()
        for (i in seq_along(items)) {
          if (!is.null(items[[i]])) {
            new_spec[[length(new_spec) + 1]] <- list(items[[i]], list())
          }
        }

        for (i in seq_along(items_done)) {
          if (!is.null(items_done[[i]])) {
            new_spec[[i]][[2]] <- items_done[[i]]
          }
        }

        # If clearing, we remove elements from the list which have been marked "done"
        if (clearing) {
          remove_vector <- c()
          for (i in seq_along(new_spec)) {
            if (length(new_spec[[i]][[2]]) > 0) {
              remove_vector <- c(remove_vector, i)
            }
          }
          new_spec <- new_spec[-remove_vector]
        }

        # Add a new item to the list
        if (adding) {
          new_spec[[length(new_spec) + 1]] <- list(new_item, list())
        }

        # Generate dynamic components with pattern matching IDs
        new_list <- list()
        if (!is.null(unlist(new_spec))) {
          for (i in seq_along(new_spec)) {
            add_list <- list(htmlDiv(list(
              dccChecklist(
                id = list("index" = i, "type" = "done"),
                options = list(
                  list("label" = "", "value" = "done")
                ),
                value = new_spec[[i]][[2]],
                style = list("display" = "inline"),
                labelStyle = list("display" = "inline")
              ),
              htmlDiv(new_spec[[i]][[1]], id = list("index" = i, "type" = "check"), style = if (length(new_spec[[i]][[2]]) == 0) style_todo else style_done)
            ), style = list("clear" = "both")))
            new_list <- c(new_list, add_list)
          }
          return(list(new_list, ""))
        } else {
          return(list(list(), ""))
        }
      }
    )

    app$callback(
      output(id = list("index" = MATCH, "type" = "check"), property = "style"),
      params = list(
        input(id = list("index" = MATCH, "type" = "done"), property = "value")
      ),
      mark_done <- function(done){
        if (length(done[[1]] > 0)) return(style_done) else return(style_todo)
      }
    )

    app$callback(
      output(id = "totals", property = "children"),
      params = list(
        input(list("index" = ALL, "type" = "done"), property = "value"),
        state(list("index" = ALL, "type" = "check"), property = "children")
      ),
      show_totals <- function(done, total) {
        count_all = length(total)
        count_done = length(done)

        result = sprintf("%s of %s items completed", count_done, count_all)

        if (count_all > 0) {
          result = paste(result, sprintf(" - %s%%", as.integer(100 * count_done/count_all)))
        }

        if (is.null(total[[1]])) {
          return("Add an item to the list to get started.")
        } else {
          return(result)
        }
      }
    )

    app$run_server()

Dash To-Do list
0 of 0 items completed

Todo App Without Partial Updates

> This example has not been ported to R yet - showing the Python version instead.

> Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21

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

    app = Dash()

    app.layout = html.Div([
        html.Div('Dash To-Do list'),
        dcc.Input(id="new-item"),
        html.Button("Add", id="add"),
        html.Button("Clear Done", id="clear-done"),
        html.Div(id="list-container"),
        html.Div(id="totals")
    ])

    style_todo = {"display": "inline", "margin": "10px"}
    style_done = {"textDecoration": "line-through", "color": "#888"}
    style_done.update(style_todo)

    @callback(
        [
            Output("list-container", "children"),
            Output("new-item", "value")
        ],
        [
            Input("add", "n_clicks"),
            Input("new-item", "n_submit"),
            Input("clear-done", "n_clicks")
        ],
        [
            State("new-item", "value"),
            State({"index": ALL}, "children"),
            State({"index": ALL, "type": "done"}, "value")
        ]
    )
    def edit_list(add, add2, clear, new_item, items, items_done):
        triggered = [t["prop_id"] for t in ctx.triggered]
        adding = len([1 for i in triggered if i in ("add.n_clicks", "new-item.n_submit")])
        clearing = len([1 for i in triggered if i == "clear-done.n_clicks"])
        new_spec = [
            (text, done) for text, done in zip(items, items_done)
            if not (clearing and done)
        ]
        if adding:
            new_spec.append((new_item, []))
        new_list = [
            html.Div([
                dcc.Checklist(
                    id={"index": i, "type": "done"},
                    options=[{"label": "", "value": "done"}],
                    value=done,
                    style={"display": "inline"},
                    labelStyle={"display": "inline"}
                ),
                html.Div(text, id={"index": i}, style=style_done if done else style_todo)
            ], style={"clear": "both"})
            for i, (text, done) in enumerate(new_spec)
        ]
        return [new_list, "" if adding else new_item]

    @callback(
        Output({"index": MATCH}, "style"),
        Input({"index": MATCH, "type": "done"}, "value")
    )
    def mark_done(done):
        return style_done if done else style_todo

    @callback(
        Output("totals", "children"),
        Input({"index": ALL, "type": "done"}, "value")
    )
    def show_totals(done):
        count_all = len(done)
        count_done = len([d for d in done if d])
        result = "{} of {} items completed".format(count_done, count_all)
        if count_all:
            result += " - {}%".format(int(100 * count_done / count_all))
        return result

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