Custom Pagination with interval paginationGoTo - Update Active Page

I have used the example code to add a Custom Pagination to my AG Grid.

I have then added a call back to loop through the pages:

@app.callback(
    Output("custom-pagination-grid", "paginationGoTo", allow_duplicate=True),
    Input('update', 'n_intervals'),
    State("custom-pagination-grid", "paginationInfo"),
    prevent_initial_call=True
)
def auto_pagination(timer, pagination_info):
    total_pages = pagination_info["totalPages"]
    current_page = pagination_info["currentPage"]

    if timer % 2 == 0:
        if current_page + 1 == total_pages:
            return 0
        else:
            return current_page + 1
    else:
        return no_update

I had hoped I could use

Ouput("custom-pagination", "active_page")

and return with the same logic as paginationGoTo but that doesn’t seem to work

Hello @QSD449,

Not exactly sure what your goal is here.

Could you please explain more about your use-case?

1 Like

Hi @jinnyzor, thank you for your reply.

My goal is to have the dashboard on a communal screen that automatically cycles through the different pages (which it currently does) but at the same time updates the custom pagination controls to show the active page, which it would do if they were clicked, so you can tell which page is currently being shown during the auto cycling.

Hopefully this is clearer.

Yes, this is helpful.

Can you give your code with example data?

1 Like

Here you go, it is based off of the example from here

The idea is the highlighting of the page number changes as the pages automatically cycle as it does if you click a certain page number.
image

The below code works for the auto cycling

import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, no_update, callback, State
import pandas as pd
import dash_bootstrap_components as dbc

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])


df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

# basic columns definition with column defaults
columnDefs = [
    {"field": "country"},
    {"field": "year"},
    {"field": "athlete"},
    {"field": "sport"},
    {"field": "total"},
]

app.layout = html.Div(
    [
        dcc.Interval(id='update', n_intervals=0, interval=1000 * 3),
        dcc.Markdown(
            "Example of custom pagination controls with Dash Bootstrap Components `dbc.Pagination`"
        ),
        dbc.Pagination(
            id="custom-pagination",
            max_value=87,
            first_last=True,
            previous_next=True,
            size="sm",
            fully_expanded=False,
        ),
        dag.AgGrid(
            id="custom-pagination-grid",
            columnDefs=columnDefs,
            rowData=df.to_dict("records"),
            columnSize="sizeToFit",
            defaultColDef={"resizable": True, "sortable": True, "filter": True},
            dashGridOptions={
                "pagination": True,
                "suppressPaginationPanel": True,
                "suppressScrollOnNewData": True,
            },
        ),
    ],
    style={"margin": 20},
)


@callback(
    Output("custom-pagination", "max_value"),
    Input("custom-pagination-grid", "paginationInfo"),
)
def update_pagination_control(pagination_info):
    if pagination_info is None:
        return no_update
    return pagination_info["totalPages"]


@callback(
    Output("custom-pagination-grid", "paginationGoTo"),
    Input("custom-pagination", "active_page"),
    prevent_initial_call=True
)
def goto_page(n):
    if n is None or n == 1:
        return "first"
    # grid pagination starts at zero
    return n - 1


@app.callback(
    Output("custom-pagination-grid", "paginationGoTo", allow_duplicate=True),
    Input('update', 'n_intervals'),
    State("custom-pagination-grid", "paginationInfo"),
    prevent_initial_call=True
)
def auto_pagination(timer, pagination_info):
    total_pages = pagination_info["totalPages"]
    current_page = pagination_info["currentPage"]

    if timer % 2 == 0:
        if current_page + 1 == total_pages:
            return 0
        else:
            return current_page + 1
    else:
        return no_update


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

Alright, here is a working version.

Instead of the intervals manipulating the Grid, I have it manipulating the pagination control:

import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, no_update, callback, State
import pandas as pd
import dash_bootstrap_components as dbc

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])


df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

# basic columns definition with column defaults
columnDefs = [
    {"field": "country"},
    {"field": "year"},
    {"field": "athlete"},
    {"field": "sport"},
    {"field": "total"},
]

app.layout = html.Div(
    [
        dcc.Interval(id='update', n_intervals=0, interval=1000 * 3),
        dcc.Markdown(
            "Example of custom pagination controls with Dash Bootstrap Components `dbc.Pagination`"
        ),
        dbc.Pagination(
            id="custom-pagination",
            max_value=87,
            first_last=True,
            previous_next=True,
            size="sm",
            fully_expanded=False,
        ),
        dag.AgGrid(
            id="custom-pagination-grid",
            columnDefs=columnDefs,
            rowData=df.to_dict("records"),
            columnSize="sizeToFit",
            defaultColDef={"resizable": True, "sortable": True, "filter": True},
            dashGridOptions={
                "pagination": True,
                "suppressPaginationPanel": True,
                "suppressScrollOnNewData": True,
            },
        ),
    ],
    style={"margin": 20},
)


@callback(
    Output("custom-pagination", "max_value"),
    Input("custom-pagination-grid", "paginationInfo"),
)
def update_pagination_control(pagination_info):
    if pagination_info is None:
        return no_update
    return pagination_info["totalPages"]


@callback(
    Output("custom-pagination-grid", "paginationGoTo"),
    Input("custom-pagination", "active_page"),
    prevent_initial_call=True
)
def goto_page(n):
    if n is None or n == 1:
        return "first"
    # grid pagination starts at zero
    return n - 1


@app.callback(
    Output("custom-pagination", "active_page"),
    Input('update', 'n_intervals'),
    State("custom-pagination-grid", "paginationInfo"),
    prevent_initial_call=True
)
def auto_pagination(timer, pagination_info):
    total_pages = pagination_info["totalPages"]
    current_page = pagination_info["currentPage"]

    if timer % 2 == 0:
        if current_page + 1 == total_pages:
            return 0 + 1
        else:
            return current_page + 2
    else:
        return no_update


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

The tricky part is the grid starts at 0 while the dbc pagination starts at 1, thus I had to shift them. :slight_smile:

3 Likes

Thank you, this is great!

To confirm my understanding, is the updated callback (auto_pagination) calling the goto_page callback to change the page?

The interval changes the active_page, then the active_page changes the grid. Basically a chained callback.

1 Like

Hi @jinnyzor

Do you know if there is a way to use Persistence for the Pagination?

I assume not as I am using the dbc.Paginantion component.

You can always try.

I don’t think so though, because you’d have to use paginationGoTo, there isn’t anything that takes it from the paginationInfo.

With that said, the pagination dbc component should have persistence.