Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Bugs with pagination/selections when number of data rows changes #924

Open

Description

@samlishak-artemis

A few different bugs can be demonstrated with the following app.

It just shows a button and a table; the table has 10 rows if the button has been clicked an odd number of times, and 30 otherwise. The table shows 10 rows per page. Each row has an ID, which is incremented by 1 for every row when there are 30 rows, and incremented by 2 otherwise.

import random

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

app = dash.Dash(__name__)


COLUMNS = ["id", "a", "b", "c", "d"]

app.layout = html.Div(
    [
        html.Button(
            "Change data",
            id="change",
            n_clicks=0,
        ),
        dash_table.DataTable(
            id="table",
            columns=[{"name": x, "id": x} for x in COLUMNS],
            page_action="native",
            page_size=10,
            row_selectable="multi",
        ),
    ]
)


@app.callback(
    Output("table", "data"),
    Input("change", "n_clicks"),
)
def change_data(n_clicks):
    if n_clicks % 2:
        rows = 10
        id_step = 2
    else:
        rows = 30
        id_step = 1

    return [generate_row(i * id_step) for i in range(rows)]


def generate_row(id):
    return {x: f"id-{id}" if x == "id" else random.randint(0, 10) for x in COLUMNS}


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

Table disappears when selected page no longer exists

  1. While the table has 30 rows, navigate to page 2 or 3 of the table
  2. Click the "change" button. There is now only 1 page, so the page navigation does not appear. There is also no data on the current selected (invalid) page, so no data is shown either, just the column headers.

There doesn't appear to be a way out of this situation. Maybe it could be fixed by a workaround where a callback updates page_current based on data and page_size, but it might be neater if it was automatically handled by the component.

Uncaught TypeError: Cannot read property 'id' of undefined after a row has been selected then removed

  1. While the table has 30 rows, navigate to page 2 or 3 of the table
  2. Tick some of the rows
  3. Navigate back to page 1 of the table
  4. Click the "change" button
  5. Tick one of the rows. No tickbox appears and the following error is written to the console:
Uncaught TypeError: Cannot read property 'id' of undefined
    at operations.tsx:32
    at r (_map.js:7)
    at map.js:62
    at _dispatchable.js:44
    at Object.t (_curry2.js:28)
    at onChange (operations.tsx:32)
    at HTMLUnknownElement.callCallback (react-dom@16.v1_9_1m1617985068.14.0.js:182)
    at Object.invokeGuardedCallbackDev (react-dom@16.v1_9_1m1617985068.14.0.js:231)
    at invokeGuardedCallback (react-dom@16.v1_9_1m1617985068.14.0.js:286)
    at invokeGuardedCallbackAndCatchFirstError (react-dom@16.v1_9_1m1617985068.14.0.js:300)

I think this is because selected_rows contains row indices that are outside of the current data. Again, maybe it could be worked around with a callback to update selected_rows based on data.

Incorrect rows remain selected when data changes

  1. While the table has 30 rows, select the rows with ID "id-2" and "id-4"
  2. Click the "change" button. Now the rows with ID "id-4" and "id-8" are selected, but selected_row_ids is still ["id-2", "id-4"].

selected_rows appears to be the "primary" way of remembering which rows are selected, and selected_row_ids just follows on from that. In this situation, it seems like using storing the row IDs would make more sense, as this example represents a sort of filtering action where some rows have been removed from the table, so the user would want the same row IDs selected after filtering.

Having selected_row_ids as the "primary" way of storing the selected rows rather than selected_rows also fixes a related issue I'm having where I want to modify selected_row_ids from another callback, but it won't actually update the tickboxes because selected_rows isn't changing.

Activity

  1. changed the title [-]Bugs with pagination when number of data rows changes[/-] [+]Bugs with pagination/selections when number of data rows changes[/+] on Jul 14, 2021
  2. ismafoot commented on Sep 29, 2021

    @ismafoot

    Hi, I've encountered a similar issue:
    Context:
    I have a DataTable that gets updated by two different dropdown callbacks with multi select enabled.
    Let's say that based on my dropdrown values, I have 4 rows showing in the table and I select the last row.

    When I change a value in one of my dropdowns, my table now shows 2 rows. If I try to select one of those rows, I can't.

    In order to make it work , I have to go back to the previous dropdown value combinations, then unselect that row and then change the dropdowns.

    Chrome Error Message:
    image

    Sample data:

    
    import dash
    
    import dash_core_components as dcc
    
    import dash_html_components as html
    
    from dash.dependencies import Input, Output
    
    import pandas as pd 
    
    from dash_table import DataTable
    
    app = dash.Dash()
    
    country = pd.Series(['germany', 'france','germany', 'france'])
    
    city = pd.Series(['munich', 'paris','berlin', 'lyon'])
    
    pop = pd.Series([100, 200,300, 400])
    
    frame = pd.DataFrame({'country': country, 'city':city, 'pop': pop})
    
    app.layout = html.Div([
    
            html.Div([
    
                dcc.Dropdown(
    
                id='country',
    
                value = 'germany',
    
                clearable=False,
    
                options = [{'label': str(c), 'value': c} for c in frame.country.unique()],
    
                multi=False
    
            ),
    
            dcc.Dropdown(
    
                id='city',
    
                clearable=True,
    
                multi=True
    
            )]),
    
            html.Div([
    
                DataTable(
    
                    id='tbl',
    
                    columns=[{"name": "...", "id": "..."}],
    
                    row_selectable='multi'
    
                    )])
    
    ])
    
    # populate simulation dropdown filter
    
    @app.callback(
    
        Output('city', 'options'),
    
        Input('country', 'value')
    
    )
    
    def update_filter(country):
    
        new_frame = frame[(frame.country == country)]
    
        opt = [{'label': str(c), 'value': c} for c in new_frame.city.unique()]
    
        return opt
    
    @app.callback(
    
        Output('tbl', 'columns'),
    
        Output('tbl', 'data'),
    
        Input('country', 'value'),
    
        Input('city', 'value')
    
    )
    
    def update_table(country, city):
    
        # session descriptive info
    
        new_frame = frame[(frame.country == country) & (frame.city.isin(city))]
    
        cols = [{'name': i.capitalize(), 'id': i} for i in new_frame.columns]
    
        data = new_frame.to_dict('records')
    
        return cols, data
    
    if __name__ == '__main__':
    
        app.run_server(debug=True)
    
    
  3. niels-de-witte commented on Jun 22, 2022

    @niels-de-witte

    @AnnMarieW Are there any updates on the issue relating update/removing rows and it's relationship with selected row attribute? Feels like this can be a real bottleneck for applications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions