AG Grid Enterprise Rich Cell Select Editor

This question was asked on GitHub, so I’m posting it here to make it easier to find. This isn’t in the Dash AG Grid docs yet, but it’s available in Dash AG Grid >= 31.0.0

Note that the Rich Select Cell Editor is an AG Grid Enterprise feature. Find out more information on AG Grid Licensing and pricing in the AG Grid Doc.

Note that the cool color cell renderer (and many many other great features) ares available in the Free AG Grid Community version :slight_smile:

Rich Select Cell Editor

An AG Grid Enterprise Feature

An alternative to using the browser’s select popup for dropdowns inside the grid.

The Rich Select Cell Editor allows users to enter a cell value from a list of provided values by searching or filtering the list.

Allow Typing

The editor input can be configured to allow text input, which is used to match different parts of the editor list items as shown below:
ag-grid-rich-cell-select


from dash import Dash, html
import dash_ag_grid as dag
import pandas as pd

app = Dash(__name__)


colors = [
  'AliceBlue',
  'AntiqueWhite',
  'Aqua',
  'Aquamarine',
  'Azure',
  'Beige',
  'Bisque',
  'Black',
  'BlanchedAlmond',
  'Blue',
  'BlueViolet',
  'Brown',
  'BurlyWood',
  'CadetBlue',
  'Chartreuse',
  'Chocolate',
  'Coral',
  'CornflowerBlue',
  'Cornsilk',
  'Crimson',
  'Cyan',
  'DarkBlue',
  'DarkCyan',
  'DarkGoldenrod',
  'DarkGray',
  'DarkGreen',
  'DarkGrey',
  'DarkKhaki',
  'DarkMagenta',
  'DarkOliveGreen',
  'DarkOrange',
  'DarkOrchid',
  'DarkRed',
  'DarkSalmon',
  'DarkSeaGreen',
  'DarkSlateBlue',
  'DarkSlateGray',
  'DarkSlateGrey',
  'DarkTurquoise',
  'DarkViolet',
  'DeepPink',
  'DeepSkyBlue',
  'DimGray',
  'DodgerBlue',
  'FireBrick',
  'FloralWhite',
  'ForestGreen',
  'Fuchsia',
  'Gainsboro',
  'GhostWhite',
  'Gold',
  'Goldenrod',
  'Gray',
  'Green',
  'GreenYellow',
  'Grey',
  'Honeydew',
  'HotPink',
  'IndianRed',
  'Indigo',
  'Ivory',
  'Khaki',
  'Lavender',
  'LavenderBlush',
  'LawnGreen',
  'LemonChiffon',
  'LightBlue',
  'LightCoral',
  'LightCyan',
  'LightGoldenrodYellow',
  'LightGray',
  'LightGreen',
  'LightGrey',
  'LightPink',
  'LightSalmon',
  'LightSeaGreen',
  'LightSkyBlue',
  'LightSlateGray',
  'LightSlateGrey',
  'LightSteelBlue',
  'LightYellow',
  'Lime',
  'LimeGreen',
  'Linen',
  'Magenta',
  'Maroon',
  'MediumAquamarine',
  'MediumBlue',
  'MediumOrchid',
  'MediumPurple',
  'MediumSeaGreen',
  'MediumSlateBlue',
  'MediumSpringGreen',
  'MediumTurquoise',
  'MediumVioletRed',
  'MidnightBlue',
  'MintCream',
  'MistyRose',
  'Moccasin',
  'NavajoWhite',
  'Navy',
  'OldLace',
  'Olive',
  'OliveDrab',
  'Orange',
  'OrangeRed',
  'Orchid',
  'PaleGoldenrod',
  'PaleGreen',
  'PaleTurquoise',
  'PaleVioletRed',
  'PapayaWhip',
  'PeachPuff',
  'Peru',
  'Pink',
  'Plum',
  'PowderBlue',
  'Purple',
  'Rebeccapurple',
  'Red',
  'RosyBrown',
  'RoyalBlue',
  'SaddleBrown',
  'Salmon',
  'SandyBrown',
  'SeaGreen',
  'Seashell',
  'Sienna',
  'Silver',
  'SkyBlue',
  'SlateBlue',
  'SlateGray',
  'SlateGrey',
  'Snow',
  'SpringGreen',
  'SteelBlue',
  'Tan',
  'Teal',
  'Thistle',
  'Tomato',
  'Turquoise',
  'Violet',
  'Wheat',
  'White',
  'WhiteSmoke',
  'Yellow',
  'YellowGreen',
]
df = pd.DataFrame({"color": colors})

columnDefs = [
  {
    'headerName': 'Allow Typing (Match)',
    'field': 'color',
    'cellRenderer': 'ColourCellRenderer',
    'cellEditor': 'agRichSelectCellEditor',
    'cellEditorParams': {
      'values': colors,
      'searchType': 'match',
      'allowTyping': True,
      'filterList': True,
      'highlightMatch': True,
      'valueListMaxHeight': 220,
    },
  },
  {
    'headerName': 'Allow Typing (MatchAny)',
    'field': 'color',
    'cellRenderer': 'ColourCellRenderer',
    'cellEditor': 'agRichSelectCellEditor',
    'cellEditorParams': {
      'values': colors,
      'searchType': 'matchAny',
      'allowTyping': True,
      'filterList': True,
      'highlightMatch': True,
      'valueListMaxHeight': 220,
    },
  }
]

app.layout = html.Div(
    [
        dag.AgGrid(
            enableEnterpriseModules=True,
        #    licenseKey =  Your license key here
            columnDefs=columnDefs,
            defaultColDef={"editable": True},
            rowData=df.to_dict('records'),
            columnSize="sizeToFit",
            dashGridOptions={"animateRows": False}
        ),
    ]
)


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


"""
Add the following to the dashAgGridComponentFunctions.js file in the assets folder:

 var dagcomponentfuncs = (window.dashAgGridComponentFunctions =
    window.dashAgGridComponentFunctions || {});

 dagcomponentfuncs.ColourCellRenderer = (props) => {
  
  const styles = {
    verticalAlign: "middle",
    border: "1px solid black",
    margin: 3,
    display: "inline-block",
    width: 20,
    height: 10,
    backgroundColor: props.value.toLowerCase(),
  };
  return React.createElement("div", {}, [
    React.createElement("span", { style: styles }),
    React.createElement("span", {}, props.value),
  ]);
};


"""



dag-docs

3 Likes

Hello @AnnMarieW
I’ve managed to allow typing on a select cell editor, while setting hardcoded values on the python-side, as you did in your example:

'cellEditorParams': {
      'values': colors,

But when I am trying to achieve the same while using a JS function, I can’t manage to make it work:

'cellEditorParams': {
    'headerName': 'Allow Typing (MatchAny)',
    'field': 'my_field',
    'function': 'myCustomJSFunction(params.data.my_custom_data_col)',
    'searchType': 'matchAny',
    'allowTyping': True,
    'filterList': True,
    'highlightMatch': True
}

Is it yet possible to combine typing and generate cell possible values through a JS function?

Thank you
Davy

Hi @davy.datascience

You should be able to use a JS function. but it would probably need to look something like

'cellEditorParams': {
      'values': {"function": "myCustomJSFunction(params)"}

If that doesn’t work could you provide a complete minimal example with some sample data?

Hi @AnnMarieW

Thanks for your fast reply.

I still do not manage to make it work. Here I’ve build a minimal example of dropdown options with JS working without the free text typing:

from dash import Dash, html
import dash_ag_grid as dag
import pandas as pd

app = Dash(__name__)

df = pd.DataFrame(
    {
        "color": ["red", "blue", "yellow"],
        "color_options": [["red", "blue"], ["blue", "brown"], ["yellow", "orange"]]
    }
)
    
columns_def = [
    {"headerName": "ID", "field": "id", "editable": False, "type": "rightAligned"},
    {
        "headerName": "color",
        "field": "color",
        "cellEditor": "agRichSelectCellEditor",
        "cellEditorPopup": True,
        "cellEditorParams": {"function": "getColorOptions(params.data.color_options)"},
        "filter": "agSetColumnFilter"
    }
]

app.layout = html.Div(
    [
        dag.AgGrid(
        	id="activity-table",
        	getRowId="params.data.id",
        	rowData=df.to_dict("records"),
        	columnDefs=columns_def,
        	columnSize="autoSize",
        	columnSizeOptions={"keys": []},
        	defaultColDef={
        	    "editable": True,
        	    "resizable": True,
        	    "sortable": True,
        	    "floatingFilter": True,
        	    "suppressMovable": True,
        	    "menuTabs": ["filterMenuTab", "columnsMenuTab"],
        	},
        	dashGridOptions={
        	    "rowSelection": "multiple",
        	    "singleClickEdit": False,
        	    "enableRangeSelection": True,
        	    "enableFillHandle": True,
        	    "undoRedoCellEditing": True,
        	    "stopEditingWhenCellsLoseFocus": True,
        	    "tooltipShowDelay": 100,
        	},
        	enableEnterpriseModules=True,
        	# licenseKey= Your license key here
        	style={"height": "1000px"},
        ),
    ]
)

But when I try to implement the free text typing it doesn’t work at all:

from dash import Dash, html
import dash_ag_grid as dag
import pandas as pd

app = Dash(__name__)

df = pd.DataFrame(
    {
        "color": ["red", "blue", "yellow"],
        "color_options": [["red", "blue"], ["blue", "brown"], ["yellow", "orange"]]
    }
)

columns_def = [
    {"headerName": "ID", "field": "id", "editable": False, "type": "rightAligned"},
    {
        "headerName": "color",
        "field": "color",
        "cellEditor": "agRichSelectCellEditor",
        "cellEditorPopup": True,
        'cellEditorParams': {
            'values': {"function": "getColorOptions(params.data.color_options)"},
            'searchType': 'matchAny',
            'allowTyping': True,
            'filterList': True,
            'highlightMatch': True,
            'valueListMaxHeight': 220,
        },
        "filter": "agSetColumnFilter"
    }
]

app.layout = html.Div(
    [
        dag.AgGrid(
        	id="activity-table",
        	getRowId="params.data.id",
        	rowData=df.to_dict("records"),
        	columnDefs=columns_def,
        	columnSize="autoSize",
        	columnSizeOptions={"keys": []},
        	defaultColDef={
        	    "editable": True,
        	    "resizable": True,
        	    "sortable": True,
        	    "floatingFilter": True,
        	    "suppressMovable": True,
        	    "menuTabs": ["filterMenuTab", "columnsMenuTab"],
        	},
        	dashGridOptions={
        	    "rowSelection": "multiple",
        	    "singleClickEdit": False,
        	    "enableRangeSelection": True,
        	    "enableFillHandle": True,
        	    "undoRedoCellEditing": True,
        	    "stopEditingWhenCellsLoseFocus": True,
        	    "tooltipShowDelay": 100,
        	},
        	enableEnterpriseModules=True,
        	# licenseKey= Your license key here
        	style={"height": "1000px"},
        ),
    ]
)

Here’s the JS code associated:

var dagfuncs = window.dashAgGridFunctions = window.dashAgGridFunctions || {};

dagfuncs.getColorOptions = function(colorOptions) {
    return {
        values: colorOptions,
    }
}

Thanks in advance

Hi @davy.datascience

Oh, I see - providing a complete example is helpful.

Start with your first example and keep this the same (disregard my earlier answer)

 "cellEditorParams": {"function": "getColorOptions(params.data.color_options)"},

Then change the JS function to:


dagfuncs.getColorOptions = function(colorOptions) {
    return {
        values: colorOptions,
        allowTyping: true,
        filterList: true,
        highlightMatch: true,
    }
}

1 Like

Thank you @AnnMarieW it works like a charm! :smiley:

Hi @AnnMarieW ,

I’m having trouble to display the full list of options (using the valueListMaxHeight param).

You’ll find below the code as well as a screenshot of the issue:

***dashAgGridComponentsFunctions.js***
dagfuncs.getOptions = function(options) {

    return {
        values: options,
        allowTyping: true,
        filterList: true,
        highlightMatch: true,
        valueListMaxHeight: 500
    }
}

***column_def***
CORRECTION_DEFINED_DEF = custom_column_def(
    field=CORRECTION_DEFINED,
    header_name='Corrected Value (Defined Choice)',
    cell_style={
        'styleConditions': [
            {
                'condition': "params.data.column == 'Area'",
                'style': {'backgroundColor': 'grey'},
            },
            {
                'condition': "params.data.column != 'Area'",
                'style': {'backgroundColor': ECOACT_GREEN_LIGHT},
            },
        ],
    },
    cell_editor='agRichSelectCellEditor',
    cell_editor_params={'function': 'getOptions(params.data.options)',
                        'valueListMaxHeight': 300},
    editable={'function': "params.data.column != 'Area'"}
)

def custom_column_def(field: str,
                      header_name: Optional[str] = None,
                      width: Optional[int] = None,
                      dag_type: Optional[DagTypes] = None,
                      sortable: Optional[bool] = False,
                      editable: bool | Dict = False,
                      value_formatter: Optional[str] = None,
                      value_getter: Optional[str] = None,
                      cell_renderer: Optional[str] = None,
                      cell_renderer_params: Optional[Dict] = None,
                      cell_style: Optional[Dict] = None,
                      cell_editor: Optional[str] = None,
                      cell_editor_params: Optional[Dict] = None) -> Dict[str, Any]:
    """
    Creates a custom def dict
    """
    return {
        'field': field,
        'headerName': header_name or field.replace('_', ' ').title(),
        'width': width,
        'filter': DAG_FILTERS.get(dag_type, True),  # type: ignore[arg-type]
        'sortable': sortable,
        'editable': editable,
        'type': DAG_TYPES.get(dag_type),  # type: ignore[arg-type]
        'valueFormatter': get_value_formatter(dag_type, value_formatter),
        'valueGetter': {'function': value_getter},
        'filterValueGetter': {'function': "d3.timeParse('%d/%m/%Y')(params.data.date)"} if
        dag_type == DagTypes.DATE else None,
        'cellRenderer': cell_renderer,
        'cellRendererParams': cell_renderer_params,
        'tooltipField': field,
        'cellStyle': cell_style,
        'cellEditor': cell_editor,
        'cellEditorParams': cell_editor_params
    }

I can’t seem to be able to show more than one possible choice:

Could you please help me?
Best,
Yoann

Hi @yoann_diep

Can you make a complete minimal example I can run that will reproduce the issue in the image?

Did you run the example app in the initial post? Does that work for you?

Sure, tell me if you need more

PAGE_ID = f"{'-'.join(DATA_VALIDATION.split('_'))}-{PAGE}"
layout = dmc.Container(id=PAGE_ID, fluid=True)

@callback(
    Output(PAGE_ID, CHILDREN),
    Input(TOKEN, DATA)
)
def render_data_validation_page(token: Dict) -> html.Div | List:
    """
    Renders the Data Validation page
    """
    page = dmc.Stack([
        html.Div(id=DATA_VALIDATION_DIV),
        dmc.LoadingOverlay(
            visible=False,
            id=DATA_VALIDATION_LOADING_OVERLAY,
            overlayProps={"radius": "sm", "blur": 2},
            zIndex=10,
        ),
    ])

    return page

@callback(
    Output(DATA_VALIDATION_DIV, CHILDREN),
    Input(PORTFOLIO_STORE, DATA),
    Input(YEAR_STORE, DATA),
    Input(SCENARIO_STORE, DATA),
    running=[(Output(DATA_VALIDATION_LOADING_OVERLAY, 'visible'), True, False)]
)
def render_data_validation_div(portfolio: str,
                               year: str,
                               scenario: str) -> dmc.Stack | None:
    """
    Renders the Data Validation tables
    """

    row_data = [{OPTIONS: ['Yes', 'No', 'Unknown']}]

    column_def = {'field': 'correction_defined', 'headerName': 'Corrected Value (Defined Choice)',
      'width': None, 'filter': True, 'sortable': False, 'editable': {'function': "params.data.column != 'Area'"}, 'type': None,  'valueFormatter': None, 'valueGetter': {'function': None}, 'filterValueGetter': None, 'cellRenderer': None,  'cellRendererParams': None, 'tooltipField': 'correction_defined', 'cellStyle': {'styleConditions': [{'condition': "params.data.column == 'Area'", 'style': {'backgroundColor': 'grey'}}, {'condition': "params.data.column != 'Area'", 'style': {'backgroundColor': '#bed1ad'}}]}, 'cellEditor':'agRichSelectCellEditor', 'cellEditorParams': {'function': 'getOptions(params.data.options)', 'valueListMaxHeight': 300}}

   default_col_def =  {
        # make every column use 'text' filter by default
        # enable floating filters by default
        'floatingFilter': False,
        # make columns resizable
        'resizable': True,
        'sortable': True,
        'wrapHeaderText': True,
        'autoHeaderHeight': True,
        'wrapText': True,
        'autoHeight': False,
        # Make columns editable
        'editable': True,
        'filter': 'agSetColumnFilter',
        "suppressMovable": True,
        "menuTabs": ["filterMenuTab", "columnsMenuTab"],

    }
   row_style =  {
        'styleConditions': [
            {
                'condition': 'params.node.rowIndex % 2 === 1',
                'style': {'backgroundColor': row_alternating_color},
            },
        ]
    }

   dash_grid_options =  {
        'colResizeDefault': 'shift',
        'rowSelection': 'single',
        'headerHeight': 30,
        'groupHeaderHeight': 30,
        'pagination': True,
        'paginationPageSize': pagination_page_size,
        'suppressRowHoverHighlight': True,
        'animateRows': False
    }


    return dag.AgGrid(
        id='data-validation-table',
        enableEnterpriseModules=True,
        licenseKey=DAG_ENTERPRISE_AUTH.dag_license_key,
        columnDefs=column_def,
        rowData=row_data,
        defaultColDef=default_col_def,
        getRowStyle=row_style,
        columnSize='responsiveSizeToFit',
        dashGridOptions=dash_grid_options,
        className='ag-theme-material',
        persistence=True,
    )

I’ve tried the example app and I’m getting the same issue

I just tried the example and it worked fine. Are you using AG Grid Enterprise (license required)?
What version of Dash AG Grid are you using?

Here’s a live example on PyCafe

I’ve upgraded my version of dash-ag-grid and it works now! Thanks!

1 Like