plotly / dash-ag-grid

Dash AG Grid is a high-performance and highly customizable component that wraps AG Grid, designed for creating rich datagrids.
https://dash.plotly.com/dash-ag-grid
MIT License
176 stars 26 forks source link

AG Grid callback function does not recognize children component of Markdown output #305

Closed baa-ableton closed 4 months ago

baa-ableton commented 4 months ago

Hello!

We are using Vizro which in turn uses Plotly components. We have the following code:

import vizro.models as vm
import vizro.plotly.express as px
from vizro import Vizro
from vizro.actions import filter_interaction
from vizro.models.types import capture
import pandas as pd 
from vizro.tables import dash_ag_grid

import json # Testing

data = pd.read_csv('./develop/survey.csv')
questions = pd.DataFrame(data['question'].drop_duplicates())
segments = pd.DataFrame(list(data[data['question_type']=='single_choice']['question'].unique()) + ['none'], columns = ['segment'])

@capture("action")
def my_custom_action(clickedcell: dict):
    """Custom action."""
   # text = json.dumps(clickedcell)

    #return lambda: f"You clicked on cell with value: {text}"  # Return an update function
    return json.dumps(clickedcell) if clickedcell else "Click on a point on the above graph(func)."

df = segments

page = vm.Page(
    title="Example of a custom action with UI inputs and outputs",
    path="custom_actions",
    id="custom_actions",
    layout=vm.Layout(
        grid=[
            [0, 1]
        ],
        row_gap="25px",
    ),
    components=[
        vm.AgGrid(
            id = "cell-selection-simple-click-callback"
            , figure=dash_ag_grid(
                id = "cell-selection-simple-click-callback",
                data_frame = df,
                rowData=df.to_dict("records"),
                columnDefs=[{"field": i} for i in df.columns],
                defaultColDef={"filter": True},
                columnSize="sizeToFit",
                getRowId="params.data.State",
                dashGridOptions={"animateRows": False}
            )
            , actions=[
                vm.Action(function=my_custom_action(),
                    inputs=["cell-selection-simple-click-callback.cellClicked"],
                    outputs=["my_card.children"], 
                ),
            ],
        ),
        vm.Card(id="my_card", text="Click on a point on the above graph."),
    ],
)

✅ The grid loads up well ✅ The card component reacts once I single-click on a row

The problem is, that the card never gets populated with the desired string, which is to say any sort of JSON (I also tried accessing the value property but without success). The card is always populated with "Click on a point on the above graph(func)."

And on the developer console I note the following error:

"Property "children" was used with component ID:
  "cell-selection-simple-click-callback"
in one of the Output items of a callback.
This ID is assigned to a dash_ag_grid.AgGrid component
in the layout, which does not support this property.
This ID was used in the callback(s) for Output(s):
  action_finished.data@1d38fd52d26aec87386d6afdc36f8591e0f2631abef5506348e9dceea2d55a5e, cell-selection-simple-click-callback.children@1d38fd52d26aec87386d6afdc36f8591e0f2631abef5506348e9dceea2d55a5e"

Just for reference, I created the same code as (here)[https://vizro.readthedocs.io/en/stable/pages/user-guides/custom-actions/#interact-with-dashboard-inputs-and-outputs] which uses inputs and outputs to different components (albeit scatter_plot and card components) on the same system which worked exactly as expected.

Am I setting up the grid incorrectly or do you think this could be a bug?

Thank you!

BSd3v commented 4 months ago

Hello @baa-ableton,

This is a misconfiguration on your end, here is a working (complete) example:

import vizro.models as vm
import vizro.plotly.express as px
from vizro import Vizro
from vizro.actions import filter_interaction
from vizro.models.types import capture
import pandas as pd
from vizro.tables import dash_ag_grid
import vizro.plotly.express as px

import json  # Testing

# data = pd.read_csv('./develop/survey.csv')
# questions = pd.DataFrame(data['question'].drop_duplicates())
# segments = pd.DataFrame(list(data[data['question_type']=='single_choice']['question'].unique()) + ['none'], columns = ['segment'])

@capture("action")
def my_custom_action(clickedcell: dict):
    """Custom action."""
    # text = json.dumps(clickedcell)

    # return lambda: f"You clicked on cell with value: {text}"  # Return an update function
    return (
        json.dumps(clickedcell)
        if clickedcell
        else "Click on a point on the above graph(func)."
    )

df = px.data.gapminder()

page = vm.Page(
    title="Example of a custom action with UI inputs and outputs",
    path="custom_actions",
    id="custom_actions",
    layout=vm.Layout(
        grid=[[0, 1]],
        row_gap="25px",
    ),
    components=[
        vm.AgGrid(
            id="cell-selection-simple-click-callback",
            figure=dash_ag_grid(
                id="cell-selection-simple-click-callback-grid",
                data_frame=df,
                rowData=df.to_dict("records"),
                columnDefs=[{"field": i} for i in df.columns],
                defaultColDef={"filter": True},
                columnSize="sizeToFit",
                getRowId="params.data.State",
                dashGridOptions={"animateRows": False},
            ),
            actions=[
                vm.Action(
                    function=my_custom_action(),
                    inputs=["cell-selection-simple-click-callback-grid.cellClicked"],
                    outputs=["my_card.children"],
                ),
            ],
        ),
        vm.Card(id="my_card", text="Click on a point on the above graph."),
    ],
)

dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()

Please note, I added a differentiator in the grid's id as associated the action with the new id.

As an aside, the getRowId I dont think that State exists in your data, therefore this getRowId is not useful.

antonymilne commented 4 months ago

Thank you for posting the solution @BSd3v and thank you @AnnMarieW for pointing this out! (I work on the Vizro team)

Your error here is very understandable and I think we should have something in place to ensure others don't fall into the same trap. Basically what's happening here is:

  1. vm.Graph works "nicely" because the id there is one id set in vm.Graph(id=...) that works when the graph is used as output (e.g. for a vm.Filter) and input (e.g. for an action)
  2. vm.AgGrid and vm.Table are awkward because there's two separate ids: vm.AgGrid(id="outer", figure=dash_ag_grid(id="inner")). The outer one is what's used when the grid is used as an output and the inner one when it's used as an input

For case 2 by default you are not expected to set an inner id, so we set inner id to something that's different from the outer one. This works nicely for built-in actions like filter_interaction but when you define a custom action it's a problem because you don't know what this inner id is so you have to set it explicitly yourself.

If you're thinking this is all very confusing then you are right 😅 This is something we would like to improve. But just for now, the gist of it is that both outer and inner ids are optional but if you set them both then they need to be different.

@petar-qb @maxschulz-COL please say if I missed something here, but I think this is something we overlooked with the input_id scheme. Best solution for now is just to put in some validation that ensures figure.id is not the same as self.id in the AgGrid model I guess.

petar-qb commented 4 months ago

Hi @baa-ableton and thanks for pointing this out.

@antonymilne you're right and I agree with you that we should validate that the figure.id is not the same as self.id for vm.AgGrid and vm.Table models.

I agree that all the inner/outer ID stuff can be confusing, so we'll improve or explain it better in our docs along with some examples.

P.S @BSd3v Thanks for answering this question. It's contributions like yours that make this community so great 🎉

baa-ableton commented 4 months ago

@BSd3v thank you very much for the quick help. We have it working on our end now 🙏