2025, Oct 20 23:17

Мигание ячеек в AG Grid и NiceGUI: почему не срабатывает и как починить

Почему в AG Grid с NiceGUI не срабатывает мигание ячеек при обновлении данных. Решение: getRowId, setDataValue и enableCellChangeFlash. Подробный разбор.

Мигание ячеек при обновлении в AG Grid — небольшой, но полезный штрих UX: когда значения меняются, взгляд пользователя естественно притягивается к тому, что отличается. Однако при интеграции AG Grid в NiceGUI с бэкендом на Pandas DataFrame легко настроить нужные опции и всё равно не увидеть запуска анимации. Главная загвоздка — в способе обновления грида.

Как воспроизвести проблему

Ниже пример: таблица с колонками Name и Score, включено мигание при изменении значений, а после изменения данных предпринимается попытка программно подсветить изменённые ячейки. Для наглядности стандартный зелёный цвет заменён на красный.

from nicegui import ui
import pandas as pd, random

# A) Подключить HighlightChangesModule
ui.add_head_html('''
<script>
  const { ModuleRegistry, HighlightChangesModule } = window.agGrid;
  ModuleRegistry.registerModules([ HighlightChangesModule ]);
</script>
''')

# B) Принудительно задать красный цвет вспышки в теме Alpine
ui.add_head_html('''
<style>
  .ag-theme-alpine .ag-cell-data-changed {
    background-color: red !important;
    color: white !important;
    transition: background-color 0.5s ease;
  }
</style>
''')

# C) Пример данных
dataset = pd.DataFrame({'id':[0,1,2], 'name': ['Alice','Bob','Charlie'], 'score': [10,20,30]})
prior = dataset.copy()

# D) Таблица с включённым миганием при изменении
with ui.column().classes('w-full ag-theme-alpine'):
    table = ui.aggrid(
        {
            "columnDefs": [{"field": "id"}, {"field": "name"}, {"field": "score", 'enableCellChangeFlash': True}],
            "rowData": dataset.to_dict("records"),
            "animateRows": True,
            "cellFlashDelay": 500,
            "cellFadeDelay": 1000,
            'deltaRowDataMode': True,
            # ':getRowId': 'params => params.data.id',
        }
    )

# E) Обновить очки: переписать данные, перерисовать таблицу и попытаться подсветить изменённые ячейки
def bump_scores():
    global dataset, prior
    prior = dataset.copy()
    dataset['score'] = [random.randint(0,100) for _ in dataset.index]

    table.options['rowData'] = dataset.to_dict('records')
    table.update()

    changed_cells = []
    for i in dataset.index:
        if dataset.at[i,'score'] != prior.at[i,'score']:
            changed_cells.append({'rowIndex': i, 'column': 'score'})
    table.run_method('api.flashCells', {'cells': changed_cells})

ui.button('Update Scores', on_click=bump_scores)
ui.run(host="127.0.0.1")

Что на самом деле идёт не так

Обновление грида — и есть виновник. Когда вся таблица перерисовывается, внутреннего состояния «это значение только что изменилось», на основе которого AG Grid применяет временный класс подсветки, на новом DOM уже нет. Иными словами, table.update() запускает полный перерисовку, из‑за чего грид перестаёт различать прежнее и новое значение в конкретной ячейке, и класс для мигания не применяется.

Отдельно: если в процессе отладки вы наткнулись на битую ссылку в документации, это тоже не поможет. А когда что‑то упорно не работает, откройте консоль DevTools в браузере и проверьте, нет ли ошибок JavaScript — это хороший быстрый тест.

Рабочий обходной путь для стабильного мигания

Вместо перерисовки всего грида обновляйте значения ячеек на месте. Дайте AG Grid стабильные идентификаторы строк через getRowId, включите enableCellChangeFlash и используйте API грида, чтобы устанавливать новые значения в изменённых ячейках. Тогда AG Grid увидит дельту и применит встроенную анимацию. Цвет можно настроить через CSS‑переменные, чтобы сохранить красный фон и белый текст вспышки.

from nicegui import ui
import pandas as pd
import random

records = pd.DataFrame({
    'id': [0, 1, 2],
    'name': ['Alice', 'Bob', 'Charlie'],
    'score': [10, 20, 30],
})

ui.add_head_html('''
<style>
  :root {
    --ag-value-change-value-highlight-bg-color: red;
    --ag-value-change-value-highlight-color: white;
  }
  .ag-theme-alpine .ag-cell-data-changed,
  .ag-cell-data-changed-animation {
    background-color: var(--ag-value-change-value-highlight-bg-color) !important;
    color: var(--ag-value-change-value-highlight-color) !important;
  }
</style>
''')

with ui.column().classes('w-full ag-theme-alpine'):
    sheet = ui.aggrid.from_pandas(records, options={
        'columnDefs': [
            {'headerName': col.capitalize(), 'field': col}
            for col in records.columns
        ],
        'cellFlashDuration': 800,
        'cellFadeDuration': 1500,
        'defaultColDef': {'enableCellChangeFlash': True},
        ':getRowId': 'params => params.data.id.toString()',
    })

def apply_new_scores():
    for _, rec in records.iterrows():
        nid = rec['id']
        new_val = random.randint(0, 100)
        records.loc[records['id'] == nid, 'score'] = new_val
        sheet.run_row_method(str(nid), 'setDataValue', 'score', new_val)

ui.button('Update Scores', on_click=apply_new_scores)
ui.run(host='127.0.0.1')

Почему это важно

Сохранив экземпляр грида и меняя только изменившиеся значения, вы позволяете AG Grid делать то, для чего он создан: находить отличия и анимировать их. Так вы не боретесь с жизненным циклом компонента и избавляетесь от необходимости имитировать мигание, вручную выискивая изменённые ячейки после полной перерисовки.

Что стоит запомнить

Если нужны анимации при изменении ячеек, не обновляйте весь грид. Стабилизируйте идентификаторы строк, включите встроённое мигание и вызывайте setDataValue через мост NiceGUI, чтобы AG Grid отслеживал изменения на уровне ячеек. Если подозреваете другие проблемы, быстро проверьте корректность ссылки на документацию и загляните в консоль браузера на наличие ошибок. С этими настройками столбец Score мигает стабильно, а внимание пользователя остаётся на том, что изменилось.

Статья основана на вопросе с StackOverflow от Iain MacCormick и ответе Detlef.