From d8ca514e0afce20485c8711e60aef267b4d33c4a Mon Sep 17 00:00:00 2001 From: Rodja Trappe Date: Thu, 2 Oct 2025 06:24:07 +0200 Subject: [PATCH 1/3] ui.sub_pages can be refreshed --- nicegui/elements/sub_pages.py | 8 +++++++ nicegui/sub_pages_router.py | 13 +++++++++++ tests/test_sub_pages.py | 43 +++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/nicegui/elements/sub_pages.py b/nicegui/elements/sub_pages.py index 86a8d2ddc0..96a2c2c664 100644 --- a/nicegui/elements/sub_pages.py +++ b/nicegui/elements/sub_pages.py @@ -67,6 +67,14 @@ def add(self, path: str, page: Callable) -> Self: self._show() return self + def refresh(self) -> None: + """Rebuild this sub pages element. + + *Added in version 3.XX.X* + """ + self._reset_match() + self._show() + def _show(self) -> None: """Display the page matching the current URL path.""" self._rendered_path = '' diff --git a/nicegui/sub_pages_router.py b/nicegui/sub_pages_router.py index 7e3785e6b1..64c7fe0a24 100644 --- a/nicegui/sub_pages_router.py +++ b/nicegui/sub_pages_router.py @@ -49,6 +49,19 @@ def on_path_changed(self, handler: Callable[[str], None]) -> None: """ self._path_changed_handlers.append(handler) + async def refresh(self) -> None: + """Refresh the currently shown sub pages. + + This will clear and rebuild the current sub page as if navigating to it again. + Useful when you want to update the page content based on changes in data or state. + + *Added in version 3.XX.X* + """ + for el in context.client.layout.descendants(): + if isinstance(el, SubPages): + el._reset_match() # pylint: disable=protected-access + await self._handle_open(self.current_path) + async def _handle_open(self, path: str) -> bool: self.current_path = path self.is_initial_request = False diff --git a/tests/test_sub_pages.py b/tests/test_sub_pages.py index c82c375b81..fd49742b58 100644 --- a/tests/test_sub_pages.py +++ b/tests/test_sub_pages.py @@ -1169,3 +1169,46 @@ def index(): screen.click('Delete') screen.wait(0.5) screen.should_not_contain('main page') + + +def test_refresh_sub_page(screen: Screen): + calls = {'index': 0, 'outer': 0, 'inner_main': 0, 'inner_other': 0} + + @ui.page('/') + @ui.page('/{_:path}') + def index(): + calls['index'] += 1 + sub_pages = ui.sub_pages({'/': outer_page}) + ui.button('Refresh via Router', on_click=ui.context.client.sub_pages_router.refresh) + ui.button('Refresh via SubPages', on_click=sub_pages.refresh) + + def outer_page(): + calls['outer'] += 1 + ui.sub_pages({'/': inner_main, '/other': inner_other}) + ui.link('Go to other', '/other') + + def inner_main(args: PageArguments): + calls['inner_main'] += 1 + ui.button('Refresh inner', on_click=args.frame.refresh) + + def inner_other(args: PageArguments): + calls['inner_other'] += 1 + ui.button('Refresh inner', on_click=args.frame.refresh) + + screen.open('/') + assert calls == {'index': 1, 'outer': 1, 'inner_main': 1, 'inner_other': 0} + + screen.click('Refresh inner') + assert calls == {'index': 1, 'outer': 1, 'inner_main': 2, 'inner_other': 0} + + screen.click('Go to other') + assert calls == {'index': 1, 'outer': 1, 'inner_main': 2, 'inner_other': 1} + + screen.click('Refresh inner') + assert calls == {'index': 1, 'outer': 1, 'inner_main': 2, 'inner_other': 2} + + screen.click('Refresh via Router') + assert calls == {'index': 1, 'outer': 2, 'inner_main': 2, 'inner_other': 3} + + screen.click('Refresh via SubPages') + assert calls == {'index': 1, 'outer': 3, 'inner_main': 2, 'inner_other': 4} From 97b6f999f0dc25cc8bb02a7f8c9d01ee28262d87 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Wed, 15 Oct 2025 17:46:00 +0200 Subject: [PATCH 2/3] use more specific button labels --- tests/test_sub_pages.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_sub_pages.py b/tests/test_sub_pages.py index fd49742b58..60cb1152fb 100644 --- a/tests/test_sub_pages.py +++ b/tests/test_sub_pages.py @@ -1189,22 +1189,22 @@ def outer_page(): def inner_main(args: PageArguments): calls['inner_main'] += 1 - ui.button('Refresh inner', on_click=args.frame.refresh) + ui.button('Refresh inner main', on_click=args.frame.refresh) def inner_other(args: PageArguments): calls['inner_other'] += 1 - ui.button('Refresh inner', on_click=args.frame.refresh) + ui.button('Refresh inner other', on_click=args.frame.refresh) screen.open('/') assert calls == {'index': 1, 'outer': 1, 'inner_main': 1, 'inner_other': 0} - screen.click('Refresh inner') + screen.click('Refresh inner main') assert calls == {'index': 1, 'outer': 1, 'inner_main': 2, 'inner_other': 0} screen.click('Go to other') assert calls == {'index': 1, 'outer': 1, 'inner_main': 2, 'inner_other': 1} - screen.click('Refresh inner') + screen.click('Refresh inner other') assert calls == {'index': 1, 'outer': 1, 'inner_main': 2, 'inner_other': 2} screen.click('Refresh via Router') From e8b6a606a4f6c936293654969041e7ee47c9af06 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Wed, 15 Oct 2025 17:46:07 +0200 Subject: [PATCH 3/3] add version information --- nicegui/elements/sub_pages.py | 2 +- nicegui/sub_pages_router.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nicegui/elements/sub_pages.py b/nicegui/elements/sub_pages.py index 96a2c2c664..43670ca568 100644 --- a/nicegui/elements/sub_pages.py +++ b/nicegui/elements/sub_pages.py @@ -70,7 +70,7 @@ def add(self, path: str, page: Callable) -> Self: def refresh(self) -> None: """Rebuild this sub pages element. - *Added in version 3.XX.X* + *Added in version 3.1.0* """ self._reset_match() self._show() diff --git a/nicegui/sub_pages_router.py b/nicegui/sub_pages_router.py index 64c7fe0a24..81a8049919 100644 --- a/nicegui/sub_pages_router.py +++ b/nicegui/sub_pages_router.py @@ -55,7 +55,7 @@ async def refresh(self) -> None: This will clear and rebuild the current sub page as if navigating to it again. Useful when you want to update the page content based on changes in data or state. - *Added in version 3.XX.X* + *Added in version 3.1.0* """ for el in context.client.layout.descendants(): if isinstance(el, SubPages):