Skip to content

Fix echart zoom reset on data update by using getOption() API#5822

Merged
falkoschindler merged 1 commit intomainfrom
echart
Feb 19, 2026
Merged

Fix echart zoom reset on data update by using getOption() API#5822
falkoschindler merged 1 commit intomainfrom
echart

Conversation

@falkoschindler
Copy link
Contributor

Motivation

Fixes #5819.

Updating echart data (e.g. appending points via a timer) always resets interactive state like dataZoom slider positions. This is because this.chart.options is not a valid ECharts API — it's always undefined — so the notMerge flag evaluates to true on every update, causing a full replacement of chart state instead of a merge.

MRE:

chart = ui.echart({
    'xAxis': {},
    'yAxis': {},
    'dataZoom': [{'type': 'slider', 'yAxisIndex': 0}],
    'series': [{'type': 'line', 'data': []}],
})

ui.timer(1, lambda: chart.options['series'][0]['data'].append(len(chart.options['series'][0]['data'])))

Implementation

Replace this.chart.options?.series?.length with this.chart.getOption()?.series?.length in echart.js. getOption() is the correct ECharts API to retrieve the current chart configuration. This way notMerge is only true when the number of series actually changes (requiring a clean replacement), and false for normal data updates (preserving zoom, slider positions, and other interactive state).

Progress

  • I chose a meaningful title that completes the sentence: "If applied, this PR will..."
  • The implementation is complete.
  • This is not a security issue.
  • Pytests are not necessary (would be pretty hard).
  • Documentation is not necessary.

@falkoschindler falkoschindler added this to the 3.8 milestone Feb 19, 2026
@falkoschindler falkoschindler added bug Type/scope: Incorrect behavior in existing functionality review Status: PR is open and needs review labels Feb 19, 2026
Copy link
Collaborator

@evnchn evnchn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MRE fails on main works on this PR.

getOption is not in docs but in the source code.

@falkoschindler
Copy link
Contributor Author

See link in the PR description: https://echarts.apache.org/en/api.html#echartsInstance.getOption

@evnchn
Copy link
Collaborator

evnchn commented Feb 19, 2026

Haha, I was searching in https://echarts.apache.org/en/option.html instead, so came up empty. Anyways...

@evnchn
Copy link
Collaborator

evnchn commented Feb 19, 2026

@falkoschindler I was asking Claude Code to review this as well, and it came up with this:

"""Test for PR #5822: echart zoom should not reset on data update.

Run with: pytest tests/test_pr5822.py -xvs
"""
from nicegui import ui
from nicegui.testing import Screen


def test_datazoom_preserved_on_data_update(screen: Screen):
    """Verify that dataZoom slider position is NOT reset when chart data is updated via timer."""

    @ui.page('/')
    def page():
        chart = ui.echart({
            'xAxis': {'type': 'value'},
            'yAxis': {},
            'dataZoom': [{'type': 'slider', 'yAxisIndex': 0}],
            'series': [{'type': 'line', 'data': [[i, i * 2] for i in range(20)]}],
        })

        # Button to set zoom to a custom range
        async def set_zoom():
            chart.run_chart_method(
                'dispatchAction',
                {'type': 'dataZoom', 'dataZoomIndex': 0, 'start': 20, 'end': 80},
            )
        ui.button('Set Zoom', on_click=set_zoom)

        # Single label to show current zoom state (overwritten each time)
        zoom_label = ui.label('ZoomState: unread')

        async def read_zoom():
            result = await chart.run_chart_method(':getOption')
            dz = result.get('dataZoom', [{}])[0]
            start = dz.get('start', -1)
            end = dz.get('end', -1)
            zoom_label.set_text(f'ZoomState: {start:.0f}-{end:.0f}')

        ui.button('Read Zoom', on_click=read_zoom)

        # Button to add data (simulates what the timer does in the MRE)
        def add_data():
            n = len(chart.options['series'][0]['data'])
            for i in range(5):
                chart.options['series'][0]['data'].append([n + i, (n + i) * 2])
            chart.update()
        ui.button('Add Data', on_click=add_data)

    screen.open('/')
    screen.wait(1)

    # Step 1: Set zoom to 20-80
    screen.click('Set Zoom')
    screen.wait(0.5)

    # Step 2: Read zoom to confirm it was set
    screen.click('Read Zoom')
    screen.should_contain('ZoomState: 20-80')

    # Step 3: Add data multiple times (this triggers chart.update -> setOption)
    screen.click('Add Data')
    screen.wait(0.3)
    screen.click('Add Data')
    screen.wait(0.3)
    screen.click('Add Data')
    screen.wait(0.5)

    # Step 4: Read zoom again — should still be ~20-80, NOT 0-100
    screen.click('Read Zoom')
    screen.wait(0.5)
    screen.should_contain('ZoomState: 20-80')
    screen.should_not_contain('ZoomState: 0-100')

But I don't think we want to go to that extreme of a test.

@falkoschindler falkoschindler added this pull request to the merge queue Feb 19, 2026
Merged via the queue into main with commit 9e46d49 Feb 19, 2026
7 checks passed
@falkoschindler falkoschindler deleted the echart branch February 19, 2026 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Type/scope: Incorrect behavior in existing functionality review Status: PR is open and needs review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Echart data update always resets zoom settings to default

2 participants

Comments