Allow creating a new page per client
See original GitHub issueDiscussed in https://github.com/zauberzeug/nicegui/discussions/61
<div type='discussions-op-text'>Originally posted by falkoschindler August 26, 2022
Idea: ui.page
can not only be used as context, but also accepts a Callable
argument for re-creating the page whenever a client connects.
If the path if /
or omitted, the page replaces the main page.
This is related to https://github.com/zauberzeug/nicegui/issues/6, where @me21 posted a draft for a PrivatePage
, which I haven’t looked into, yet.</div>
After discussing several concepts, we tend to break with with ui.page
contexts and move towards @ui.page
decorators. This simplifies defining page factory functions and is similar to how Flask and FastAPI define routes. In the end, pages are more like routes rather than hierarchical UI elements. Thus defining them like routes makes a lot of sense.
Also, in the flavor of Flask, FastAPI or JustPy, pages should be “private” by default, i.e. without shared state between different clients. A page should only be “shared” when an optional decorator argument is set True
.
This change has interesting consequences:
- Pages are no longer built using (possibly nested!)
with
expressions. Thus, the (internal)page_stack
is obsolete. This removes one source of confusion. - The pre-evaluation of
ui.run
is no longer needed. Since the main script primarily defines functions, we don’t need to bother about re-evaluation in case of auto-reload being activated. This complies with the traditional way of running uvicorn and removes another confusing “feature” of NiceGUI. - The decorated page functions can be async.
To keep NiceGUI very accessible for new Python developers and to reduce boilerplate code for simple single-page applications, we plan to automatically create an index page if there is none. So the NiceGUI hello-world example should remain unchanged:
from nicegui import ui
ui.label('Hello, world!')
ui.run()
This will be equivalent to the following:
from nicegui import ui
@ui.page('/')
def index_page():
ui.label('Hello, world!')
ui.run()
TODOs
- get rid of
page_stack
- remove context enter/exit from pages class
- use a default main page for ui elements created “globally”
- find better place to create default main page (and maybe remove code duplication with
ui.page
factory) - find way to set head and body html for each page
- allow configuring pages through parameters for
ui.page
- find way to apply default configurations passed in
ui.run
to default main page (which already has been created) - reloading implicitly created index page causes “No page to load” and other server errors
- show 404 page for routes which do not exist (or at least avoid 500 error)
- allow on_connect handler with sessions for non-shared pages (see sessions demo on
main.py
) - allow passing page builder functions to
ui.link
andui.open
- test/fix await/run java script per page
- remove pre-evaluation of
ui.run
- how to exclude costly imports without pre-evaluation of
ui.run
? - let JustPy serve Highcharts and Aggrid on demand
- create routes for additional libraries on demand
- introduce environment variable “MATPLOTLIB” to avoid its costly import (don’t import if it’s “false”)
- how to dynamically add a first custom element? routes to js files are added on startup.
- how to dynamically add a first chart or table? js files are excluded from the template.
- update documentation
- fix memory leak for private pages -> this is a starlette issue
- what to do with page routes which are created after startup?
- rethink the strategy of page-reload after adding js dependencies
- NiceGUI serves wrong template folder
- dependencies are not found when running with
reload=False
- do we trigger too many reloads after adding dependencies? --> we do not reload anymore
- how to add dependencies on private pages that loose their state when reloading? --> make excludes explicit again
- handle new UI elements in an event callback without container (see Color Theming and JavaScript examples)
- fix wrong title and favicon
Issue Analytics
- State:
- Created a year ago
- Comments:36 (32 by maintainers)
In https://github.com/zauberzeug/nicegui/commit/ee0fb9bdc5abcf4b86b152c9899a7e8dc4b79e85 I introduced separate view stacks per task. This solves the problem with async UI manipulation.
I thought I found a solution for injecting new dependencies into running applications. But it’s probably more complicated than running
and calling
page.update()
. When the script is large (like highcharts.js), it might not be fully loaded when the page update is called.So I got a new, much simpler idea: In the rare case of dynamically introducing a new dependency, NiceGUI can simply trigger a
location.reload()
. Since the dependency is now part of the template, it is automatically served and we don’t have to inject it ourselves.