Skip to content

Commit 13f29ac

Browse files
committed
Removed method decoration and replaced it with additional page_configs registry in Client.
General clean-up Added titles to sample app Added docu to SPA
1 parent 241215a commit 13f29ac

File tree

4 files changed

+39
-19
lines changed

4 files changed

+39
-19
lines changed

examples/session_storage/main.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from nicegui.single_page import SinglePageRouter
88

99

10-
@page('/')
10+
@page('/', title="Welcome!")
1111
def index():
1212
username = app.storage.session.get('username', '')
1313
if username == '': # redirect to login page
@@ -19,7 +19,7 @@ def index():
1919
ui.link('Logout', '/logout')
2020

2121

22-
@page('/login')
22+
@page('/login', title="Login")
2323
def login_page():
2424
def login():
2525
fake_pw_dict = {'user1': 'pw1',
@@ -45,7 +45,7 @@ def handle_login():
4545
ui.html("<small>Psst... try user1/pw1, user2/pw2, user3/pw3</small>")
4646

4747

48-
@page('/logout')
48+
@page('/logout', title="Logout")
4949
def logout():
5050
app.storage.session['username'] = ''
5151
app.storage.session['password'] = ''

nicegui/client.py

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class Client:
3232
page_routes: Dict[Callable[..., Any], str] = {}
3333
"""Maps page builders to their routes."""
3434

35+
page_configs: Dict[Callable[..., Any], "page"] = {}
36+
"""Maps page builders to their page configuration."""
37+
3538
single_page_routes: Dict[str, Any] = {}
3639
"""Maps paths to the associated single page routers."""
3740

nicegui/page.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,5 @@ async def wait_for_result() -> None:
134134

135135
self.api_router.get(self._path, **self.kwargs)(decorated)
136136
Client.page_routes[func] = self.path
137-
func.__setattr__("__ng_page", self)
137+
Client.page_configs[func] = self
138138
return func

nicegui/single_page.py

+32-15
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,50 @@
1-
import asyncio
2-
import inspect
31
from typing import Callable, Dict, Union
42

53
from fastapi.routing import APIRoute
64

75
from nicegui import background_tasks, helpers, ui, core, Client, app
8-
from nicegui.app import AppConfig
96

107

118
class RouterFrame(ui.element, component='single_page.js'):
9+
"""The RouterFrame is a special element which is used by the SinglePageRouter to exchange the content of the
10+
current page with the content of the new page. It serves as container and overrides the browser's history
11+
management to prevent the browser from reloading the whole page."""
12+
1213
def __init__(self, base_path: str):
14+
"""
15+
:param base_path: The base path of the single page router which shall be tracked (e.g. when clicking on links)
16+
"""
1317
super().__init__()
1418
self._props["base_path"] = base_path
1519

1620

1721
class SinglePageRouter:
22+
"""The SinglePageRouter allows the development of a Single Page Application (SPA) which maintains a
23+
persistent connection to the server and only updates the content of the page instead of reloading the whole page.
24+
25+
This enables the development of complex web applications with large amounts of per-user (per browser tab) data
26+
which is kept alive for the duration of the connection."""
1827

1928
def __init__(self, path: str, **kwargs) -> None:
29+
"""
30+
:param path: the base path of the single page router.
31+
"""
2032
super().__init__()
2133

2234
self.routes: Dict[str, Callable] = {}
23-
# async access lock
2435
self.base_path = path
25-
self.find_api_routes()
36+
self._find_api_routes()
2637

2738
@ui.page(path, **kwargs)
2839
@ui.page(f'{path}' + '{_:path}', **kwargs) # all other pages
2940
async def root_page(client: Client):
30-
await client.connected()
3141
if app.storage.session.get('__pageContent', None) is None:
3242
content: Union[ui.element, None] = RouterFrame(self.base_path).on('open', lambda e: self.open(e.args))
3343
app.storage.session['__pageContent'] = content
3444

35-
def find_api_routes(self):
45+
def _find_api_routes(self):
46+
"""Find all API routes already defined via the @page decorator, remove them and redirect them to the
47+
single page router"""
3648
page_routes = set()
3749
for key, route in Client.page_routes.items():
3850
if (route.startswith(self.base_path) and
@@ -46,14 +58,19 @@ def find_api_routes(self):
4658
if route.path in page_routes:
4759
core.app.routes.remove(route)
4860

49-
def add(self, path: str):
50-
def decorator(func: Callable):
51-
self.routes[path] = func
52-
return func
61+
def add(self, path: str, builder: Callable) -> None:
62+
"""Add a new route to the single page router
5363
54-
return decorator
64+
:param path: the path of the route
65+
:param builder: the builder function"""
66+
self.routes[path] = builder
5567

5668
def open(self, target: Union[Callable, str], server_side=False) -> None:
69+
"""Open a new page in the browser by exchanging the content of the root page's slot element
70+
71+
:param target: the target route or builder function
72+
:param server_side: Defines if the call is made from the server side and should be pushed to the browser
73+
history"""
5774
if isinstance(target, Callable):
5875
target = {v: k for k, v in self.routes.items()}[target]
5976
builder = target
@@ -62,9 +79,9 @@ def open(self, target: Union[Callable, str], server_side=False) -> None:
6279
return
6380
builder = self.routes[target]
6481

65-
if "__ng_page" in builder.__dict__:
66-
new_page = builder.__dict__["__ng_page"]
67-
title = new_page.title
82+
page_config = Client.page_configs.get(builder, None)
83+
if page_config is not None: # if page was decorated w/ title, favicon etc.
84+
title = page_config.title
6885
ui.run_javascript(f"document.title = '{title if title is not None else core.app.config.title}'")
6986

7087
if server_side:

0 commit comments

Comments
 (0)