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
175 stars 25 forks source link

valueFormatter does not work on initial load when in callback #113

Closed alisonmosky closed 1 year ago

alisonmosky commented 1 year ago

I am working with a variety of dataframes where I'm trying to set the valueFormatters for various columns. Given the nature of the app I'm making, both the data and the columnDefs for the table are returned to the grid object in a callback that takes multiple inputs. It appears that the valueFormatters do not register on initial loading of the table but will register once the data is reloaded when an input is changed.

Here is a video showing the behavior I'm (attempting) to describe. It is a simplified version of a larger app that I am working on.

Let me know if you need more information.

https://user-images.githubusercontent.com/40575566/227970204-411fca28-c28b-42d1-8f2a-1603c7cfa94b.mov

AnnMarieW commented 1 year ago

Hi @alisonmoss3 Thanks for reporting. Could you please post the code for the demo instead of just a video?

alisonmosky commented 1 year ago

Yep! Here is the code and the csv file is attached

import dash
from dash import html, dcc
import pandas as pd
import dash_bootstrap_components as dbc
import dash_ag_grid as dag
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__)

comp = dbc.Col([html.Label(["Comparison to Show Enriched Processes ", html.I(className="bi bi-question-circle", id = 'go-comp-info')], className="variableLabel"),
        dcc.Dropdown(
            id = 'go-comparison',
            value = None,
            clearable=False,
            optionHeight=70,
            persistence=True,
            persistence_type='session',
        )])

show_ont_table = dbc.Col([
        dbc.Card([
                    dag.AgGrid(
                        id='go-table',
                        dashGridOptions={'pagination': True, 'paginationPageSize': 100},
                        # rowData=data,  columnDefs=col_options,
                        defaultColDef={"filter": False,
                                       "resizable": True,
                                       "sortable": True,
                                       "editable": False,
                                       # "floatingFilter": True,
                                       "minWidth": 100, },
                        # columnSize='sizeToFit',
                        className="ag-theme-alpine-dark",
                    )
                  ],

        body = True)])

go_csv = pd.read_csv('gene_ontology_output.csv')

go_json = go_csv.to_json()

app.layout = html.Div([
    dcc.Store(id='ontology-data', data=go_json),
    comp,
    show_ont_table
])

@app.callback(
    Output('go-comparison','options'),
    [Input('ontology-data','data')]
)
def render_options(go_datj):
    go_dat = pd.read_json(go_datj)

    comps = list(go_dat.comparison.unique())
    comps.sort()
    comp_options=[{'label': comp, 'value': comp} for comp in comps]

    return comp_options

@app.callback(
    [Output('go-table','rowData'),
     Output('go-table', 'columnDefs'),
     ],
    [Input('ontology-data','data'),
     Input('go-comparison','value'),
])
def render_go_table(goj, comp):

    go = pd.read_json(goj)
    go = go.astype({'pvalue': float, 'p.adjust': float, 'qvalue': float})

    if comp is not None:
        dat = go[go['comparison'] == comp]
    else:
        dat = go

    dat_sub = dat

    dat_sub.drop('identifier', axis = 1, inplace = True)
    dat_sub.rename(columns={'p.adjust': 'padj'}, inplace = True)
    col_options = list(dat_sub.columns)
    col_options.remove('geneid')
    col_options.append('geneid')
    col_options = [{'headerName': i, 'field': i} for i in col_options]
    inds = [index for (index,d) in enumerate(col_options) if d['field'] in ['pvalue','p.adjust','qvalue', 'padj']]
    for ind in inds:
        col_options[ind]['valueFormatter'] = {'function': 'd3.format(".3g")(params.value)'}
    table = dat_sub.to_dict('records')

    return table, col_options

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

gene_ontology_output.csv

AnnMarieW commented 1 year ago

H @alisonmoss3 Try including the columnDefs with the valueFormatter in the grid in the layout as well as the callback

BSd3v commented 1 year ago

Hello @alisonmoss3,

I am not having an issue with this, but it may very well be fixed in the future a5 that I am running.

alisonmosky commented 1 year ago

@BSd3v I'm currently using a4 so maybe thats part of the issue?

@AnnMarieW the reason its not included in the first place is that the larger app I'm making includes more inputs that alter the columns (and data) that are shown in the table. Because of that I can't provide the columnDefs in the grid layout. Here is another video that shows some more of this behavior with the different types of tables but I am unfortunately unable to share the source code for this.

As you can see, even when I switch table types (and the callback has obviously fired), the valueFormatter doesn't seem to be working until I change the 'Gene List' input. Changing to something else and then back to what it was originally fixes the issue so it seems to just be on some kind of initial load?

https://user-images.githubusercontent.com/40575566/228075967-dc84ec75-906b-4fcf-9f1f-c6528fb262da.mov

BSd3v commented 1 year ago

Yes, we haven't released a5 yet. But it seems that it fixes this issue, though it's hard to say exactly.

alisonmosky commented 1 year ago

Interesting! I spent a while trying to figure out if it was something in my code or not because the behavior is definitely odd (but clearly working some of the time) but I couldn't really pinpoint anything that would cause that.

Happy to test anything if it helps!

BSd3v commented 1 year ago

@alisonmoss3,

Could you please confirm that this is working in a5?

alisonmosky commented 1 year ago

Just updated and checked. Working great!!! Thanks so much!!!!!