Dash DataTable Multipage Highlight Not Updating

I have a multi-page table where rows can be selected. I have it set-up to take the input of selected rows and highlight them with a different color. However, when I go to the next page, the highlighting from the first page remains.

This is surprising to me because in style_data_conditional the input for row_index seems to be the absolute row number for the table (at least for selected rows), rather than the row number tied to the viewport. Is the style_data_conditional value of row_index actually tied to the viewport index, rather than the actual row index? What is the best way to likely address this issue?

Using dash version 1.16.3 and dash_table version 4.10.1.

Below is an example code that highlights the issue I’m seeing. If you select 1 or 2 rows to highlight them on the first page, when you move to the next page that highlighting remains (incorrect behavior), though those rows aren’t flagged as being selected (which is correct behavior)

#Package Imports
#App Components
import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import pandas as pd

#Start App
app = dash.Dash(__name__) #Assumes all assets are in "./assets subdirectory"

#Test Data
df = pd.DataFrame({'a': [1,3,6,10,11,15,6], 'b': [5,5,6,8,29,30,100], 'c': [10,23,100,20,53,63,21]})

#Default Cond Style
defaultCondStyle = [
    {'if': {'row_index': 'odd'},
    'backgroundColor': '#B6EBFE'},
    {'if': {'row_index': 'even'},
    'backgroundColor': '#D6C7FB'}
]

#Setup App Layout
app.layout = html.Div([
    html.Div(children=[
        dash_table.DataTable(
            id='table',
            row_selectable='multi',
            columns=[{"name": c, "id": c} for c in df.columns],
            data=df.to_dict('records'),
            page_size=4
        )],
        id='table-container'
    )
])

#Add Highlight Functionality
@app.callback(
    Output('table', 'style_data_conditional'),
    Input('table', 'selected_rows'),
    Input('table', 'page_current')
)
def style_selected_rows(selRows, pg):
    if selRows is None:
        return defaultCondStyle
    else:
        return defaultCondStyle + [{'if': {'row_index': selRows}, 'backgroundColor': '#FBFAAC'}]

#Code to Run the App from the commandline
if __name__ == '__main__':
    app.run_server(debug=True)

Hi @kr-hansen

You can use row ids instead. More info here: # https://dash.plotly.com/datatable/interactivity

Here is an example of how it would work:

import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import pandas as pd

# Start App
app = dash.Dash(__name__)  # Assumes all assets are in "./assets subdirectory"

# Test Data
df = pd.DataFrame(
    {
        "a": [1, 3, 6, 10, 11, 15, 6],
        "b": [5, 5, 6, 8, 29, 30, 100],
        "c": [10, 23, 100, 20, 53, 63, 21],
    }
)
df = df.reset_index().rename(columns={"index": "id"})


# Default Cond Style
defaultCondStyle = [
    {"if": {"row_index": "odd"}, "backgroundColor": "#B6EBFE"},
    {"if": {"row_index": "even"}, "backgroundColor": "#D6C7FB"},
]

# Setup App Layout
app.layout = html.Div(
    [
        html.Div(
            children=[
                dash_table.DataTable(
                    id="table",
                    row_selectable="multi",
                    columns=[{"name": i, "id": i} for i in df.columns if i != "id"],
                    data=df.to_dict("records"),
                    page_size=4,
                )
            ],
            id="table-container",
        )
    ]
)

# Add Highlight Functionality
@app.callback(
    Output("table", "style_data_conditional"),
    Input("table", "derived_viewport_selected_row_ids"),
    Input("table", "page_current"),
)
def style_selected_rows(selRows, pg):
    print(selRows)
    if selRows is None:
        return defaultCondStyle
    else:
        return defaultCondStyle + [
            {
                "if": {"filter_query": "{{id}} ={}".format(i)},
                "backgroundColor": "#FBFAAC",
            }
            for i in selRows
        ]


# Code to Run the App from the commandline
if __name__ == "__main__":
    app.run_server(debug=True)

@AnnMarieW this works just how I was wanting it to. Thank you!

If possible, are you able to explain why my original didn’t work to help me understand how to use such elements going forward? Does style_data_conditional only apply to the current viewport? Or is something else going on in that case?

Hi @kr-hansen

The row_index is absolute. If you select the second row, it will always be the second row even if you sort or filter the data - or change pages.

If you specify the id, it is associated with the data, regardless of where it is in the table. I created the id with:

df= df.reset_index().rename(columns={"index": "id"})

If you have a column in your data that acts like an index or ID, you could use that instead. But you have to rename it as id for it to work.

Then in the callback, instead of using selected_rows which only contains the row_index I used "derived_viewport_selected_row_ids". This uses the ids in the df rather than the row_index

Now that I write this, I see I made a small error - I intended to use derived_virtual_selected_row_ids. Here is the difference as described in the DataTable reference:

derived_viewport_selected_row_ids ( list of string | numbers ; optional): derived_viewport_selected_row_ids represents the IDs of the selected_rows on the currently visible page.

derived_virtual_selected_row_ids ( list of string | numbers ; optional): derived_virtual_selected_row_ids represents the IDs of the selected_rows as they appear after filtering and sorting, across all pages.

Also, in the link I sent in the first post, if you scroll down the the Row IDs section, there is a good explanation and examples of how all of this works.

I hope this helps!

`

1 Like

@AnnMarieW

If you mean you intended to use derived_virtual_selected_row_ids then I’m totally on board now. Thanks for the explanation and that adjustment also works well in my code.

You are correct! I will update my last post :blush:

1 Like