Dash DataTable: highlight row when cell is selected; callback issues

Hi everyone,

I found a few examples in this forum on how to make the whole DataTable row highlighted when a cell is selected. But I am struggling to integrate that with an already-existing callback.

Here’s my code that works (no row selection function): xenosaga/app.py at master · perfectly-preserved-pie/xenosaga · GitHub

It just renders the tab with the Dash DataTable only.

And then here is the code that should highlight the selected row, but doesn’t work. It gives me

“A nonexistent object was used in an Input of a Dash callback. The id of this object is datatable-interactivity and the property is active_cell. The string ids in the current layout are: [tabs, tab-content]”

and

"ID not found in layout
Attempting to connect a callback Input item to component:
“datatable-interactivity”
but no components with that id exist in the layout.

If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
suppress_callback_exceptions=True.
This ID was used in the callback(s) for Output(s):
tab-content.children, datatable-interactivity.selected_row_indices"

The code that spawns this error is as follows:


...


style_data_conditional = [
    {
        "if": {"state": "active"},
        "backgroundColor": "rgba(150, 180, 225, 0.2)",
        "border": "1px solid blue",
    },
    {
        "if": {"state": "selected"},
        "backgroundColor": "rgba(0, 116, 217, .03)",
        "border": "1px solid blue",
    },
]

# Create the Dash DataTable for episode 3
ep3_table = dash_table.DataTable(
    columns=[
        {"name": i, "id": i} for i in ep3_df.columns
    ],
    data=ep3_df.to_dict("records"),
    tooltip_delay=0,
    tooltip_duration=None,
    id='datatable-interactivity',
    filter_action="native",
    filter_options={
        'case': 'insensitive',
        'placeholder_text': 'Type a string to search...'
    },
    sort_action="native",
    style_data_conditional=style_data_conditional,
)

# Create the tab content
tab_1 = html.Div(children=[ep1_table])
tab_3 = html.Div(children=[ep3_table])

# Create the home page layout
app.layout = dbc.Container([
  dbc.Row( # First row: title card
    [
      dbc.Col([title_card]),
    ]
  ),
  dbc.Row( # Second row: tabs/tables
    html.Div(
      [
        dbc.Col(
          [
            tabs,
            html.Div(id="tab-content")
          ]
        )
      ]
    ),
  ),
],
className = "dbc"
)

# Create the callback to update the tab content
@app.callback(
  [Output("tab-content", "children"),
  Output("datatable-interactivity", "selected_row_indices")],
  [Input("tabs", "value"),
  Input("datatable-interactivity", "active_cell")]
)
def render_content(tab, active_cell):
    if tab == "ep1":
        return tab_1, []
    elif tab == "ep3":
        if active_cell is not None:
            return tab_3, [active_cell["row"]]
        return tab_3, []

# Run the app
app.run_server(debug=True)

I know my callback is wrong, but I don’t understand how to include both tab rendering and row highlighting together…

Hi @the.oldest.house

You might find this example helpful: Dash DataTable: press on cell should highlight row - #2 by AnnMarieW

1 Like

Thanks! I was able to get that working in my app, but I had to sacrifice the tab selection callback. I’m having trouble getting both to work at the same time.

I think the reason why Dash is saying it can’t find my DataTable’s ID datatable-interactivity is because I’m dynamically rendering the DataTable based on the selection of a tab:


# Create the home page layout
app.layout = dbc.Container([
  dbc.Row( # First row: title card
    [
      dbc.Col([title_card]),
    ]
  ),
  dbc.Row( # Second row: tabs/tables
    html.Div(
      [
        dbc.Col(
          [
            tabs,
            html.Div(id="tab-content") # this is how I'm showing a DataTable based on a callback/user input
          ]
        )
      ]
    ),
  ),
],
className = "dbc"
)

As you can see, datatable-interactivity isn’t explicitly defined anywhere in the layout. Rather, the Div’s id tab-content is what contains the DataTable but that’s dynamically generated based on user input (the tab selection).

Do you know how I can make my DataTables’ IDs visible, even if it’s not explicitly defined in the app layout? Or is there another way of resolving this?
Once I do that, I think I can get it to work with this callback (correct me if I’m wrong):

# Create the callback to update the tab content
@app.callback(
    [ 
      Output("tabs-content", "children"),
      Output("datatable-interactivity", "style_data_conditional"),
    ],
    [
      Input("tabs", "value"),
      Input("datatable-interactivity", "active_cell"),
    ]
)
def render_content(tab, active_cell):
    style = style_data_conditional.copy()
    if active_cell and tab == "ep1":
      style.append(
        {
          "if": {"row_index": active_cell["row"]},
          "backgroundColor": "rgba(150, 180, 225, 0.2)",
          "border": "1px solid blue",
        },
      )
      return tab_1, style
    elif active_cell and tab == "ep3":
      style.append(
        {
          "if": {"row_index": active_cell["row"]},
          "backgroundColor": "rgba(150, 180, 225, 0.2)",
          "border": "1px solid blue",
        },
      )
      return tab_3, style

Hi @the.oldest.house

Try putting the style update in it’s own callback:


@app.callback(    
      Output("datatable-interactivity", "style_data_conditional"),
      Input("datatable-interactivity", "active_cell"),    
)
def render_content(active_cell):
    style = style_data_conditional.copy()
    if active_cell:
      style.append(
        {
          "if": {"row_index": active_cell["row"]},
          "backgroundColor": "rgba(150, 180, 225, 0.2)",
          "border": "1px solid blue",
        },
      )
    return style
    


2 Likes

Yeah! That worked! Thanks so much; I didn’t even think to split the callbacks into two… I thought we could only use one callback.

I still see an error in the F12 dev tools console (although nothing seems to actually be broken)


html
: 
"Attempting to connect a callback Input item to component:\n  \"datatable-interactivity\"\nbut no components with that id exist in the layout.\n\nIf you are assigning callbacks to components that are\ngenerated by other callbacks (and therefore not in the\ninitial layout), you can suppress this exception by setting\n`suppress_callback_exceptions=True`.\nThis ID was used in the callback(s) for Output(s):\n  datatable-interactivity.style_data_conditional"
message
: 
"ID not found in layout"```

Is this something I can fix or should I suppress the exception as it says?

Glad it worked!

Do you include suppress_callback_exceptions=True ?

app= Dash(__name__, suppress_callback_exceptions=True)

No, not at the moment. It seems easy to enough to fix but my worry is that I’m merely hiding a symptom of an error rather than solving it, although in this case I guess it doesn’t make much of a difference?

If the ID is added via a callback, this is expected behavior. :slight_smile:

Demonstrated by this line:

If you are assigning callbacks to components that are\ngenerated by other callbacks (and therefore not in the\ninitial layout), you can suppress this exception by setting\n'suppress_callback_exceptions=True'

1 Like

Just to tag on to @jinnyzor correct answer, here is some more info from the dash docs: Callback Gotchas | Dash for Python Documentation | Plotly

1 Like

Thank you both, that explains it then.
I’m all set and I’ve learned something new today. I appreciate the help!

2 Likes