Dash Ag-Grid: Selecting a row to trigger a popup, but the grid is contained in a tab container

Hi, I’m back with another Ag Grid question :slight_smile: I’m trying to make a popup appear when I click a row, like from the examples: Dash

But my grids are contained in their own tab. My code is here: xenosaga/app.py at ag-grid · perfectly-preserved-pie/xenosaga · GitHub

I’m having issues with the callback, because my grid ID is contained in its own div, which is inside its own tab… if that makes sense. I think the fact that my grid ID is “nested” is making this difficult for me.

So what I’ve tried is this:

@app.callback(
    Output("modal", "is_open"),
    Output("modal-content", "children"),
    Input("ep1_grid", "selectedRows"),
    Input("close", "n_clicks"),
)
def open_modal(selection, _):
    if ctx.triggered_id == "close":
        return False, dash.no_update
    if selection:
        return True, "You selected " + ", ".join(
            [
                "{} (model {} and price {})".format(
                    s["make"],
                    s["model"],
                    s["price"],
                )
                for s in selection
            ]
        )

    return dash.no_update, dash.no_update

I’m simply replacing the Input ID with the grid’s ID. But in the console, I’m getting

ReferenceError: A nonexistent object was used in an `Input` of a Dash callback. The id of this object is `ep1_grid` and the property is `selectedRows`. The string ids in the current layout are: [tabs, tab-content, modal, modal-content, close]

So I tried doing something else:

@app.callback(
    Output("modal", "is_open"),
    Output("modal-content", "children"),
    Input("tabs", "selectedRows"),
    Input("close", "n_clicks"),
    Input("ep1_grid", "selectedRows")
)
def open_modal(tab_selection, _, grid_selection):
    if ctx.triggered_id == "close":
        return False, dash.no_update
    if grid_selection:
        return True, "You selected " + ", ".join(
            [
                "{} (HP {} and EXP {})".format(
                    s["HP"],
                    s["EXP"],
                )
                for s in grid_selection
            ]
        )
    elif tab_selection:
        return True, "You selected a row from a different tab."
    return dash.no_update, dash.no_update

But I’m still getting the same error, and a new one:

Object { message: "Invalid prop for this component", html: 'Property "selectedRows" was used with component ID:\n  "tabs"\nin one of the Input items of a callback.\nThis ID is assigned to a dash_core_components.Tabs component\nin the layout, which does not support this property.\nThis ID was used in the callback(s) for Output(s):\n  modal.is_open, modal-content.children' }

How can I reference the ep1_grid ID from the callback? How do I expose it, if it’s nested?

Hello @the.oldest.house,

You are encountering this issue because with how your tabs are created, they technically do not have the grid as part of the layout.

I’m guessing that the grid would trigger the event just fine and you are only experiencing this issue and error message when loading the layout.

You can suppress_callback_exceptions on the callback to stop seeing this error, when it is technically expected.

1 Like

I have that already set to True in the app constructor, and the grid doesn’t trigger the event anyways so it’s not just a cosmetic problem.

Go back to the other version, the one without the tabs selectedRows.

You also need to make sure that you turn on selections, none of your grids have it.

Yes!! Thank you, this was it. I got the modal popup working, but I have two lingering questions.

  1. How can I format the modal popup? All my text is together in one line. I’d like to use newlines.
def open_modal(selection, _):
    if ctx.triggered_id == "close":
        return False, dash.no_update
    if selection:
        return True, f"""
          HP: {selection[0]['HP']}
          EXP: {selection[0]['EXP']}
          TP: {selection[0]['TP']}
          EP: {selection[0]['SP']}
          SP: {selection[0]['SP']}
          Cash: {selection[0]['Cash']}
          Normal Drop: {selection[0]['Normal Drop']}
          Rare Drop: {selection[0]['Rare Drop']}
          Type: {selection[0]['Type']}
          Weakness: {selection[0]['Weakness']}
        """

gets me
image

  1. The error still remains in my console; any way to fix it or shall I just leave it alone? Feels odd just ignoring it…
    ReferenceError: A nonexistent object was used in an Inputof a Dash callback. The id of this object isep1_gridand the property isselectedRows. The string ids in the current layout are: [tabs, tab-content, modal, modal-content, close]

You can wrap the entire output into a pre or use a markdown with carriage returns for the formatting.

And yes, try to suppress the exceptions on just that one callback.

Gotcha. I formatted the output with dcc.Markdown.

Thank you so much for your help :pray:

2 Likes

Actually, sorry, I’m getting a duplicate output error when I try to make an additional modal callback for the other grids:

# Create a callback to open a modal when a row is selected in the episode 1 grid
# Based on https://dashaggrid.pythonanywhere.com/other-examples/popup-from-cell-click
@app.callback(
  Output("modal", "is_open"),
  Output("modal-content", "children"),
  Input("ep1_grid", "selectedRows"),
  Input("close", "n_clicks"),
)
def open_modal(selection, _):
  if ctx.triggered_id == "close":
    return False, dash.no_update
  if selection:
    # Use Markdown to format the modal content
    return True, dcc.Markdown(f""" 
      **Name:** {selection[0]['Name']}  \n
      **HP:** {selection[0]['HP']}  \n
      **EXP:** {selection[0]['EXP']}  \n
      **TP:** {selection[0]['TP']}  \n
      **EP:** {selection[0]['SP']}  \n
      **SP:** {selection[0]['SP']}  \n
      **Cash:** {selection[0]['Cash']}  \n
      **Normal Drop:** {selection[0]['Normal Drop']}  \n
      **Rare Drop:** {selection[0]['Rare Drop']}  \n
      **Type:** {selection[0]['Type']}  \n
      **Weakness:** {selection[0]['Weakness']}  \n
      """)
  return dash.no_update, dash.no_update

# Create a callback to open a modal when a row is selected in the episode 2 grid
# Based on https://dashaggrid.pythonanywhere.com/other-examples/popup-from-cell-click
@app.callback(
  Output("modal", "is_open"),
  Output("modal-content", "children"),
  Input("ep2_grid", "selectedRows"),
  Input("close", "n_clicks"),
)
def open_modal(selection, _):
  if ctx.triggered_id == "close":
    return False, dash.no_update
  if selection:
    # Use Markdown to format the modal content
    return True, dcc.Markdown(f""" 
      **Name:** {selection[0]['Name']}  \n
      **HP:** {selection[0]['HP']}  \n
      **EXP:** {selection[0]['EXP']}  \n
      **CPTS:** {selection[0]['TP']}  \n
      **SPTS":** {selection[0]['SP']}  \n
      **CBoost?:** {selection[0]['SP']}  \n
      """)
  return dash.no_update, dash.no_update

Object { message: "Duplicate callback outputs", html: "In the callback for output(s):\n modal.is_open\n modal-content.children\nOutput 1 (modal-content.children) is already in use.\nAny given output can only have one callback that sets it.\nTo resolve this situation, try combining these into\none callback function, distinguishing the trigger\nby using dash.callback_context if necessary." }

I’ve tried combining them into one callback per the suggestion:

# Create a callback to open a modal when a row is selected in either of the grids
@app.callback(
    [Output("modal", "is_open"), Output("modal-content", "children")],
    [Input("ep1_grid", "selectedRows"), Input("ep2_grid", "selectedRows"), Input("close", "n_clicks")],
)
def open_modal(ep1_selection, ep2_selection, close):
    ctx = dash.callback_context
    if ctx.triggered_id == "close":
        return False, dash.no_update
    elif ctx.triggered_id == "ep1_grid" and ep1_selection:
        # Use Markdown to format the modal content for episode 1 grid
        return True, dcc.Markdown(f""" 
          **Name:** {ep1_selection[0]['Name']}  \n
          **HP:** {ep1_selection[0]['HP']}  \n
          **EXP:** {ep1_selection[0]['EXP']}  \n
          **TP:** {ep1_selection[0]['TP']}  \n
          **EP:** {ep1_selection[0]['SP']}  \n
          **SP:** {ep1_selection[0]['SP']}  \n
          **Cash:** {ep1_selection[0]['Cash']}  \n
          **Normal Drop:** {ep1_selection[0]['Normal Drop']}  \n
          **Rare Drop:** {ep1_selection[0]['Rare Drop']}  \n
          **Type:** {ep1_selection[0]['Type']}  \n
          **Weakness:** {ep1_selection[0]['Weakness']}  \n
          """)
    elif ctx.triggered_id == "ep2_grid" and ep2_selection:
        # Use Markdown to format the modal content for episode 2 grid
        return True, dcc.Markdown(f""" 
          **Name:** {ep2_selection[0]['Name']}  \n
          **HP:** {ep2_selection[0]['HP']}  \n
          **EXP:** {ep2_selection[0]['EXP']}  \n
          **CPTS:** {ep2_selection[0]['CPTS']}  \n
          """)
    return False, dash.no_update

But I’m still getting the same error.

Hi,

Pls refer to this doc. https://dash.plotly.com/duplicate-callback-outputs

Given that you are running an up-to-date version of dash you need to add the following parameter: allow_duplicate=True

1 Like