浏览代码

Merge pull request #570 from zauberzeug/spa

Providing SPA demo
Rodja Trappe 2 年之前
父节点
当前提交
9e082fc78a
共有 4 个文件被更改,包括 93 次插入0 次删除
  1. 36 0
      examples/single_page_app/main.py
  2. 40 0
      examples/single_page_app/router.py
  3. 16 0
      examples/single_page_app/router_frame.js
  4. 1 0
      main.py

+ 36 - 0
examples/single_page_app/main.py

@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+from router import Router
+
+from nicegui import ui
+
+router = Router()
+
+
+@router.add('/')
+async def show_one():
+    ui.label('Content One').classes('text-2xl')
+
+
+@router.add('/two')
+async def show_two():
+    ui.label('Content Two').classes('text-2xl')
+
+
+@router.add('/three')
+async def show_three():
+    ui.label('Content Three').classes('text-2xl')
+
+
+@ui.page('/')  # normal index page (eg. the entry point of the app)
+@ui.page('/{_:path}')  # all other pages will be handled by the router but must be registered to also show the SPA index page
+async def main():
+    # adding some navigation buttons to switch between the different pages
+    with ui.row():
+        ui.button('One', on_click=lambda: router.open(show_one)).classes('w-32')
+        ui.button('Two', on_click=lambda: router.open(show_two)).classes('w-32')
+        ui.button('Three', on_click=lambda: router.open(show_three)).classes('w-32')
+
+    # this places the content which should be displayed
+    router.frame().classes('w-full p-4 bg-gray-100')
+
+ui.run()

+ 40 - 0
examples/single_page_app/router.py

@@ -0,0 +1,40 @@
+from typing import Awaitable, Callable, Dict, Union
+
+from nicegui import background_tasks, ui
+from nicegui.dependencies import register_component
+
+register_component('router_frame', __file__, 'router_frame.js')
+
+
+class Router():
+
+    def __init__(self) -> None:
+        self.routes: Dict[str, Callable] = {}
+        self.content: ui.element = None
+
+    def add(self, path: str):
+        def decorator(func: Callable):
+            self.routes[path] = func
+            return func
+        return decorator
+
+    def open(self, target: Union[Callable, str]):
+        if isinstance(target, str):
+            path = target
+            builder = self.routes[target]
+        else:
+            path = {v: k for k, v in self.routes.items()}[target]
+            builder = target
+
+        async def build():
+            with self.content:
+                await ui.run_javascript(f'history.pushState({{page: "{path}"}}, "", "{path}")', respond=False)
+                result = builder()
+                if isinstance(result, Awaitable):
+                    await result
+        self.content.clear()
+        background_tasks.create(build())
+
+    def frame(self) -> ui.element:
+        self.content = ui.element('router_frame').on('open', lambda msg: self.open(msg['args']))
+        return self.content

+ 16 - 0
examples/single_page_app/router_frame.js

@@ -0,0 +1,16 @@
+export default {
+  template: "<div><slot></slot></div>",
+  mounted() {
+    window.addEventListener("popstate", (event) => {
+      if (event.state?.page) {
+        this.$emit("open", event.state.page);
+      }
+    });
+    const connectInterval = setInterval(async () => {
+      if (window.socket.id === undefined) return;
+      this.$emit("open", window.location.pathname);
+      clearInterval(connectInterval);
+    }, 10);
+  },
+  props: {},
+};

+ 1 - 0
main.py

@@ -246,6 +246,7 @@ The command searches for `main.py` in in your current directory and makes the ap
             example_link('Trello Cards', 'shows Trello-like cards that can be dragged and dropped into columns')
             example_link('Trello Cards', 'shows Trello-like cards that can be dragged and dropped into columns')
             example_link('Slots', 'shows how to use scoped slots to customize Quasar elements')
             example_link('Slots', 'shows how to use scoped slots to customize Quasar elements')
             example_link('Table and slots', 'shows how to use component slots in a table')
             example_link('Table and slots', 'shows how to use component slots in a table')
+            example_link('Single Page App', 'navigate without reloading the page')
 
 
     with ui.row().classes('bg-primary w-full min-h-screen mt-16'):
     with ui.row().classes('bg-primary w-full min-h-screen mt-16'):
         link_target('why')
         link_target('why')