1
- import asyncio
2
- import inspect
3
1
from typing import Callable , Dict , Union
4
2
5
3
from fastapi .routing import APIRoute
6
4
7
5
from nicegui import background_tasks , helpers , ui , core , Client , app
8
- from nicegui .app import AppConfig
9
6
10
7
11
8
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
+
12
13
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
+ """
13
17
super ().__init__ ()
14
18
self ._props ["base_path" ] = base_path
15
19
16
20
17
21
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."""
18
27
19
28
def __init__ (self , path : str , ** kwargs ) -> None :
29
+ """
30
+ :param path: the base path of the single page router.
31
+ """
20
32
super ().__init__ ()
21
33
22
34
self .routes : Dict [str , Callable ] = {}
23
- # async access lock
24
35
self .base_path = path
25
- self .find_api_routes ()
36
+ self ._find_api_routes ()
26
37
27
38
@ui .page (path , ** kwargs )
28
39
@ui .page (f'{ path } ' + '{_:path}' , ** kwargs ) # all other pages
29
40
async def root_page (client : Client ):
30
- await client .connected ()
31
41
if app .storage .session .get ('__pageContent' , None ) is None :
32
42
content : Union [ui .element , None ] = RouterFrame (self .base_path ).on ('open' , lambda e : self .open (e .args ))
33
43
app .storage .session ['__pageContent' ] = content
34
44
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"""
36
48
page_routes = set ()
37
49
for key , route in Client .page_routes .items ():
38
50
if (route .startswith (self .base_path ) and
@@ -46,14 +58,19 @@ def find_api_routes(self):
46
58
if route .path in page_routes :
47
59
core .app .routes .remove (route )
48
60
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
53
63
54
- return decorator
64
+ :param path: the path of the route
65
+ :param builder: the builder function"""
66
+ self .routes [path ] = builder
55
67
56
68
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"""
57
74
if isinstance (target , Callable ):
58
75
target = {v : k for k , v in self .routes .items ()}[target ]
59
76
builder = target
@@ -62,9 +79,9 @@ def open(self, target: Union[Callable, str], server_side=False) -> None:
62
79
return
63
80
builder = self .routes [target ]
64
81
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
68
85
ui .run_javascript (f"document.title = '{ title if title is not None else core .app .config .title } '" )
69
86
70
87
if server_side :
0 commit comments