Dash Ag Grid Infinite Row Model Filtering Blank not working

Hello developers! Thank you very much for an amazing product! But I had a stupor with the example in your documentation. Infinite Row Model | Dash for Python Documentation | Plotly

Sorting & Filtering

Here is the part of the code that in your example and mine doesn’t work at all:

elif data["type"] == "blank":
    df = df.loc[df[col].isnull()]
elif data["type"] == "notBlank":
    df = df.loc[~df[col].isnull()]

Please tell me how to teach this code to work as it should

Hello @sergeyvyazov,

Welcome to the community!

I believe that this is an issue with your data, it maybe be that your data is not truly blank (null) but could be an empty string.

@jinnyzor I checked it right away too. For clarity, here is the code from the examples. Here I added zero values. Still doesn’t work. I can’t figure out what I did wrong. Look here please.

import dash_ag_grid as dag
from dash import Dash, Input, Output, dcc, html, no_update
import pandas as pd


app = Dash(__name__)

df = pd.DataFrame({"id": [None, 1], "name": ["text", None]})

app.layout = html.Div(
    [
        dcc.Markdown("Infinite scroll with sort and filter"),
        dag.AgGrid(
            id="infinite-sort-filter-grid",
            columnSize="sizeToFit",
            columnDefs=[
                {"field": "id", "filter": "agNumberColumnFilter"},
                {"field": "name", "filter": "agTextColumnFilter"},
            ],
            defaultColDef={"sortable": True, "filter": True, "floatingFilter": True},
            rowModelType="infinite",
            dashGridOptions={
                # The number of rows rendered outside the viewable area the grid renders.
                "rowBuffer": 0,
                # How many blocks to keep in the store. Default is no limit, so every requested block is kept.
                "maxBlocksInCache": 1,
                "rowSelection": "multiple",
            },
        ),
    ],
    style={"margin": 20},
)

operators = {
    "greaterThanOrEqual": "ge",
    "lessThanOrEqual": "le",
    "lessThan": "lt",
    "greaterThan": "gt",
    "notEqual": "ne",
    "equals": "eq",
}


def filterDf(df, data, col):
    if data["filterType"] == "date":
        crit1 = data["dateFrom"]
        crit1 = pd.Series(crit1).astype(df[col].dtype)[0]
        if "dateTo" in data:
            crit2 = data["dateTo"]
            crit2 = pd.Series(crit2).astype(df[col].dtype)[0]
    else:
        crit1 = data["filter"]
        crit1 = pd.Series(crit1).astype(df[col].dtype)[0]
        if "filterTo" in data:
            crit2 = data["filterTo"]
            crit2 = pd.Series(crit2).astype(df[col].dtype)[0]
    if data["type"] == "contains":
        df = df.loc[df[col].str.contains(crit1)]
    elif data["type"] == "notContains":
        df = df.loc[~df[col].str.contains(crit1)]
    elif data["type"] == "startsWith":
        df = df.loc[df[col].str.startswith(crit1)]
    elif data["type"] == "notStartsWith":
        df = df.loc[~df[col].str.startswith(crit1)]
    elif data["type"] == "endsWith":
        df = df.loc[df[col].str.endswith(crit1)]
    elif data["type"] == "notEndsWith":
        df = df.loc[~df[col].str.endswith(crit1)]
    elif data["type"] == "inRange":
        if data["filterType"] == "date":
            df = df.loc[df[col].astype("datetime64[ns]").between_time(crit1, crit2)]
        else:
            df = df.loc[df[col].between(crit1, crit2)]
    elif data["type"] == "blank":
        df = df.loc[df[col].isnull()]
    elif data["type"] == "notBlank":
        df = df.loc[~df[col].isnull()]
    else:
        df = df.loc[getattr(df[col], operators[data["type"]])(crit1)]
    return df


@app.callback(
    Output("infinite-sort-filter-grid", "getRowsResponse"),
    Input("infinite-sort-filter-grid", "getRowsRequest"),
)
def infinite_scroll(request):
    dff = df.copy()

    if request:
        if request["filterModel"]:
            fils = request["filterModel"]
            for k in fils:
                try:
                    if "operator" in fils[k]:
                        if fils[k]["operator"] == "AND":
                            dff = filterDf(dff, fils[k]["condition1"], k)
                            dff = filterDf(dff, fils[k]["condition2"], k)
                        else:
                            dff1 = filterDf(dff, fils[k]["condition1"], k)
                            dff2 = filterDf(dff, fils[k]["condition2"], k)
                            dff = pd.concat([dff1, dff2])
                    else:
                        dff = filterDf(dff, fils[k], k)
                except:
                    pass
            dff = dff

        if request["sortModel"]:
            sorting = []
            asc = []
            for sort in request["sortModel"]:
                sorting.append(sort["colId"])
                if sort["sort"] == "asc":
                    asc.append(True)
                else:
                    asc.append(False)
            dff = dff.sort_values(by=sorting, ascending=asc)

        lines = len(dff.index)
        if lines == 0:
            lines = 1

        partial = dff.iloc[request["startRow"] : request["endRow"]]
        return {"rowData": partial.to_dict("records"), "rowCount": lines}


if __name__ == "__main__":
    app.run_server(debug=False)

I found one moment. If you select “Blank” then the filterDf function does not process the request at all.

elif data["type"] == "blank":
    print(df)

print not displayed

So, I wrote this filtering mechanism, and didnt test it with blank data, here is the proper way to filter it:

import dash_ag_grid as dag
from dash import Dash, Input, Output, dcc, html, no_update
import pandas as pd


app = Dash(__name__)

df = pd.DataFrame({"id": [None, 1], "name": ["text", None]})

app.layout = html.Div(
    [
        dcc.Markdown("Infinite scroll with sort and filter"),
        dag.AgGrid(
            id="infinite-sort-filter-grid",
            columnSize="sizeToFit",
            columnDefs=[
                {"field": "id", "filter": "agNumberColumnFilter"},
                {"field": "name", "filter": "agTextColumnFilter"},
            ],
            defaultColDef={"sortable": True, "filter": True, "floatingFilter": True},
            rowModelType="infinite",
            dashGridOptions={
                # The number of rows rendered outside the viewable area the grid renders.
                "rowBuffer": 0,
                # How many blocks to keep in the store. Default is no limit, so every requested block is kept.
                "maxBlocksInCache": 1,
                "rowSelection": "multiple",
            },
        ),
    ],
    style={"margin": 20},
)

operators = {
    "greaterThanOrEqual": "ge",
    "lessThanOrEqual": "le",
    "lessThan": "lt",
    "greaterThan": "gt",
    "notEqual": "ne",
    "equals": "eq",
}


def filterDf(df, data, col):
    if 'filter' in data:
        if data["filterType"] == "date":
            crit1 = data["dateFrom"]
            crit1 = pd.Series(crit1).astype(df[col].dtype)[0]
            if "dateTo" in data:
                crit2 = data["dateTo"]
                crit2 = pd.Series(crit2).astype(df[col].dtype)[0]
        else:
            crit1 = data["filter"]
            crit1 = pd.Series(crit1).astype(df[col].dtype)[0]
            if "filterTo" in data:
                crit2 = data["filterTo"]
                crit2 = pd.Series(crit2).astype(df[col].dtype)[0]
    if data["type"] == "contains":
        df = df.loc[df[col].str.contains(crit1)]
    elif data["type"] == "notContains":
        df = df.loc[~df[col].str.contains(crit1)]
    elif data["type"] == "startsWith":
        df = df.loc[df[col].str.startswith(crit1)]
    elif data["type"] == "notStartsWith":
        df = df.loc[~df[col].str.startswith(crit1)]
    elif data["type"] == "endsWith":
        df = df.loc[df[col].str.endswith(crit1)]
    elif data["type"] == "notEndsWith":
        df = df.loc[~df[col].str.endswith(crit1)]
    elif data["type"] == "inRange":
        if data["filterType"] == "date":
            df = df.loc[df[col].astype("datetime64[ns]").between_time(crit1, crit2)]
        else:
            df = df.loc[df[col].between(crit1, crit2)]
    elif data["type"] == "blank":
        df = df.loc[df[col].isnull()]
    elif data["type"] == "notBlank":
        df = df.loc[~df[col].isnull()]
    else:
        df = df.loc[getattr(df[col], operators[data["type"]])(crit1)]
    return df


@app.callback(
    Output("infinite-sort-filter-grid", "getRowsResponse"),
    Input("infinite-sort-filter-grid", "getRowsRequest"),
)
def infinite_scroll(request):
    dff = df.copy()

    if request:
        if request["filterModel"]:
            fils = request["filterModel"]
            for k in fils:
                try:
                    if "operator" in fils[k]:
                        if fils[k]["operator"] == "AND":
                            dff = filterDf(dff, fils[k]["condition1"], k)
                            dff = filterDf(dff, fils[k]["condition2"], k)
                        else:
                            dff1 = filterDf(dff, fils[k]["condition1"], k)
                            dff2 = filterDf(dff, fils[k]["condition2"], k)
                            dff = pd.concat([dff1, dff2])
                    else:
                        dff = filterDf(dff, fils[k], k)
                except Exception as e:
                    print(str(e))
                    pass
            dff = dff

        if request["sortModel"]:
            sorting = []
            asc = []
            for sort in request["sortModel"]:
                sorting.append(sort["colId"])
                if sort["sort"] == "asc":
                    asc.append(True)
                else:
                    asc.append(False)
            dff = dff.sort_values(by=sorting, ascending=asc)

        lines = len(dff.index)
        if lines == 0:
            lines = 1

        partial = dff.iloc[request["startRow"] : request["endRow"]]
        return {"rowData": partial.to_dict("records"), "rowCount": lines}


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

@jinnyzor Thank you very much for the prompt response and help. Your code solved my problem. I look forward to seeing in your documentation an example with the implementation of agSetColumnFilter in the Infinite Row Model with Filtering

1 Like

Do you have a link that I can look at for that? XD

Columns Country, Year. The introduction of this filter, together with the current ones in Dash Ag Grid

@sergeyvyazov,

AG grid enterprise is how this is created. They are very sneaky in how they demonstrate some of these cool features.

I’ll look to see if there is a way to use without enterprise. :stuck_out_tongue:

Doesnt look like it, but here is an example of using agSetColumnFilter:

import dash_ag_grid as dag
from dash import Dash, Input, Output, dcc, html, no_update
import pandas as pd


app = Dash(__name__)

df = pd.DataFrame({"id": [None, 1], "name": ["text", None]})

app.layout = html.Div(
    [
        dcc.Markdown("Infinite scroll with sort and filter"),
        dag.AgGrid(
            id="infinite-sort-filter-grid",
            columnSize="sizeToFit",
            columnDefs=[
                {"field": "id", "filter": "agNumberColumnFilter"},
                {"field": "name", "filter": "agSetColumnFilter", "filterParams": {'values': ['text', '1', '2']}},
            ],
            defaultColDef={"sortable": True, "filter": True, "floatingFilter": True},
            rowModelType="infinite",
            enableEnterpriseModules=True,
            dashGridOptions={
                # The number of rows rendered outside the viewable area the grid renders.
                "rowBuffer": 0,
                # How many blocks to keep in the store. Default is no limit, so every requested block is kept.
                "maxBlocksInCache": 1,
                "rowSelection": "multiple",
            },
        ),
    ],
    style={"margin": 20},
)

operators = {
    "greaterThanOrEqual": "ge",
    "lessThanOrEqual": "le",
    "lessThan": "lt",
    "greaterThan": "gt",
    "notEqual": "ne",
    "equals": "eq",
}


def filterDf(df, data, col):
    if 'filter' in data:
        if data["filterType"] == "date":
            crit1 = data["dateFrom"]
            crit1 = pd.Series(crit1).astype(df[col].dtype)[0]
            if "dateTo" in data:
                crit2 = data["dateTo"]
                crit2 = pd.Series(crit2).astype(df[col].dtype)[0]
        else:
            crit1 = data["filter"]
            crit1 = pd.Series(crit1).astype(df[col].dtype)[0]
            if "filterTo" in data:
                crit2 = data["filterTo"]
                crit2 = pd.Series(crit2).astype(df[col].dtype)[0]
    if 'type' in data:
        if data["type"] == "contains":
            df = df.loc[df[col].str.contains(crit1)]
        elif data["type"] == "notContains":
            df = df.loc[~df[col].str.contains(crit1)]
        elif data["type"] == "startsWith":
            df = df.loc[df[col].str.startswith(crit1)]
        elif data["type"] == "notStartsWith":
            df = df.loc[~df[col].str.startswith(crit1)]
        elif data["type"] == "endsWith":
            df = df.loc[df[col].str.endswith(crit1)]
        elif data["type"] == "notEndsWith":
            df = df.loc[~df[col].str.endswith(crit1)]
        elif data["type"] == "inRange":
            if data["filterType"] == "date":
                df = df.loc[df[col].astype("datetime64[ns]").between_time(crit1, crit2)]
            else:
                df = df.loc[df[col].between(crit1, crit2)]
        elif data["type"] == "blank":
            df = df.loc[df[col].isnull()]
        elif data["type"] == "notBlank":
            df = df.loc[~df[col].isnull()]
        else:
            df = df.loc[getattr(df[col], operators[data["type"]])(crit1)]
    elif data["filterType"] == "set":
        df = df.loc[df[col].isin(data['values'])]
    return df


@app.callback(
    Output("infinite-sort-filter-grid", "getRowsResponse"),
    Input("infinite-sort-filter-grid", "getRowsRequest"),
)
def infinite_scroll(request):
    dff = df.copy()

    if request:
        if request["filterModel"]:
            fils = request["filterModel"]
            for k in fils:
                try:
                    if "operator" in fils[k]:
                        if fils[k]["operator"] == "AND":
                            dff = filterDf(dff, fils[k]["condition1"], k)
                            dff = filterDf(dff, fils[k]["condition2"], k)
                        else:
                            dff1 = filterDf(dff, fils[k]["condition1"], k)
                            dff2 = filterDf(dff, fils[k]["condition2"], k)
                            dff = pd.concat([dff1, dff2])
                    else:
                        dff = filterDf(dff, fils[k], k)
                except:
                    pass
            dff = dff

        if request["sortModel"]:
            sorting = []
            asc = []
            for sort in request["sortModel"]:
                sorting.append(sort["colId"])
                if sort["sort"] == "asc":
                    asc.append(True)
                else:
                    asc.append(False)
            dff = dff.sort_values(by=sorting, ascending=asc)

        lines = len(dff.index)
        if lines == 0:
            lines = 1

        partial = dff.iloc[request["startRow"] : request["endRow"]]
        return {"rowData": partial.to_dict("records"), "rowCount": lines}


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

@jinnyzor It would be great. Thank you

Only here the values are constant. I searched in the same vein. And I did not find how to properly force the values to change depending on the dynamic content of the grid

@jinnyzor Possibly unique from col?

Yes, you can populate them based upon the df[col].unique()

@jinnyzor I will try to apply this. Thank you!

1 Like

Please tell me how to display ‘values’ from col in the window ‘SetColumnFilter’ for selection?

That is the AG Grid enterprise. XD

@jinnyzor Understand. Probably the same ‘crit’. In any case, thanks a lot for your help.

1 Like

Hello @jinnyzor ! I found out that maxNumConditions (filter option) does not work more than two in the infinite Row Model . How is it possible to get around this limitation? I would like to apply five or ten conditions to filter a column

Ah. I’ll have to rework the filtering of the data.

For grouped ors, it will need to build the df, while ands become for restrictive.

You might be able to fiddle with the function for filtering as well.