You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

389 lines
12 KiB

Routing and Multi-Page Apps
===========================
BadGUI supports creating multi-page applications with Vue Router integration, allowing you to build complex single-page applications with client-side routing.
Page Management
---------------
Pages in BadGUI are created using the ``page()`` function, which acts as both a page definer and a context manager.
Creating Pages
~~~~~~~~~~~~~~
.. code-block:: python
import badgui as bg
# Create a page with context manager
with bg.page("/", "HomePage", "Home"):
bg.label("Welcome to the Home Page")
bg.label("This is the main landing page")
**Parameters:**
- ``path``: URL path (e.g., ``"/"``, ``"/about"``, ``"/contact"``)
- ``name``: Component name (e.g., ``"HomePage"``, ``"AboutPage"``)
- ``title``: Page title displayed in browser tab (optional)
Page Context
~~~~~~~~~~~~
All components created within a page context are automatically added to that page:
.. code-block:: python
with bg.page("/about", "AboutPage", "About Us"):
# These components belong to the About page
bg.label("About Our Company")
bg.label("Founded in 2025")
with bg.row():
bg.label("Mission")
bg.label("Vision")
Navigation
----------
Link Component
~~~~~~~~~~~~~~
Create navigation between pages using the ``link()`` component:
.. code-block:: python
# Basic link
bg.link("Go to About", "/about")
# Styled as button
bg.link("Contact Us", "/contact").classes("q-btn q-btn-primary")
# With additional props
bg.link("Home", "/").props("exact").classes("nav-link")
**Generated Output:** ``<router-link to="/about">Go to About</router-link>``
Navigation Menu
~~~~~~~~~~~~~~~
Create a navigation menu across multiple pages:
.. code-block:: python
def create_nav():
\"\"\"Create consistent navigation menu.\"\"\"
with bg.row().classes("q-gutter-md q-mb-lg justify-center") as nav:
nav.classes("bg-primary text-white q-pa-md rounded")
bg.link("Home", "/").classes("text-white no-underline q-btn q-btn-flat")
bg.link("About", "/about").classes("text-white no-underline q-btn q-btn-flat")
bg.link("Services", "/services").classes("text-white no-underline q-btn q-btn-flat")
bg.link("Contact", "/contact").classes("text-white no-underline q-btn q-btn-flat")
# Use in each page
with bg.page("/", "HomePage", "Home"):
create_nav()
bg.label("Home Page Content")
with bg.page("/about", "AboutPage", "About"):
create_nav()
bg.label("About Page Content")
Route Parameters
----------------
Dynamic Routes
~~~~~~~~~~~~~~
Create routes with parameters:
.. code-block:: python
# Route with parameter
with bg.page("/user/:id", "UserPage", "User Profile"):
bg.label("User Profile Page")
bg.label("User ID will be available in Vue component")
# Navigation to dynamic route
bg.link("View User 123", "/user/123")
Nested Routes
~~~~~~~~~~~~~
Create nested route structures:
.. code-block:: python
# Parent route
with bg.page("/dashboard", "DashboardPage", "Dashboard"):
bg.label("Dashboard").classes("text-h3")
# Navigation to child routes
with bg.row().classes("q-gutter-md"):
bg.link("Analytics", "/dashboard/analytics")
bg.link("Settings", "/dashboard/settings")
bg.link("Profile", "/dashboard/profile")
# Child routes
with bg.page("/dashboard/analytics", "AnalyticsPage", "Analytics"):
bg.label("Analytics Dashboard")
bg.label("View your analytics data here")
with bg.page("/dashboard/settings", "SettingsPage", "Settings"):
bg.label("Dashboard Settings")
bg.input(placeholder="Setting 1")
bg.input(placeholder="Setting 2")
Multi-Page Application Example
------------------------------
Complete Blog Application
~~~~~~~~~~~~~~~~~~~~~~~~~~
Here's a complete multi-page blog application:
.. code-block:: python
import badgui as bg
def create_header():
\"\"\"Create site header with navigation.\"\"\"
with bg.row().classes("bg-primary text-white q-pa-md justify-between items-center") as header:
bg.label("My Blog").classes("text-h5 text-weight-bold")
with bg.row().classes("q-gutter-md"):
bg.link("Home", "/").classes("text-white q-btn q-btn-flat")
bg.link("Posts", "/posts").classes("text-white q-btn q-btn-flat")
bg.link("About", "/about").classes("text-white q-btn q-btn-flat")
bg.link("Contact", "/contact").classes("text-white q-btn q-btn-flat")
def create_footer():
\"\"\"Create site footer.\"\"\"
with bg.row().classes("bg-grey-8 text-white q-pa-lg justify-center q-mt-xl"):
bg.label("© 2025 My Blog. All rights reserved.").classes("text-body2")
# Home Page
with bg.page("/", "HomePage", "My Blog - Home"):
create_header()
# Hero section
with bg.column().classes("text-center q-pa-xl bg-gradient-to-r from-blue-500 to-purple-600 text-white"):
bg.label("Welcome to My Blog").classes("text-h2 q-mb-md")
bg.label("Sharing thoughts and ideas").classes("text-h6 q-mb-lg")
bg.link("Read Latest Posts", "/posts").classes("q-btn q-btn-lg q-btn-white text-primary")
# Featured posts
with bg.column().classes("q-pa-xl"):
bg.label("Featured Posts").classes("text-h4 q-mb-lg text-center")
with bg.row().classes("q-col-gutter-lg"):
for i in range(3):
with bg.column().classes("col-12 col-md-4"):
with bg.column().classes("bg-white rounded-lg shadow-2 q-pa-lg"):
bg.label(f"Post Title {i+1}").classes("text-h6 q-mb-md")
bg.label("Post excerpt goes here...").classes("text-body2 q-mb-md")
bg.link("Read More", f"/post/{i+1}").classes("q-btn q-btn-primary")
create_footer()
# Posts Page
with bg.page("/posts", "PostsPage", "All Posts"):
create_header()
with bg.column().classes("q-pa-xl"):
bg.label("All Posts").classes("text-h3 q-mb-lg")
# Post list
for i in range(10):
with bg.row().classes("bg-white rounded q-pa-lg q-mb-md shadow-1"):
with bg.column().classes("col-12"):
bg.label(f"Blog Post {i+1}").classes("text-h6 q-mb-sm")
bg.label("Published on January 1, 2025").classes("text-caption text-grey-6 q-mb-md")
bg.label("This is a preview of the blog post content...").classes("text-body2 q-mb-md")
bg.link("Read Full Post", f"/post/{i+1}").classes("q-btn q-btn-outline")
create_footer()
# Individual Post Page
with bg.page("/post/:id", "PostPage", "Blog Post"):
create_header()
with bg.column().classes("q-pa-xl max-width-md mx-auto"):
bg.label("Blog Post Title").classes("text-h3 q-mb-md")
bg.label("Published on January 1, 2025 by Author").classes("text-caption text-grey-6 q-mb-lg")
bg.label("Blog post content goes here...").classes("text-body1 q-mb-lg")
bg.label("More content and paragraphs...").classes("text-body1 q-mb-lg")
with bg.row().classes("q-mt-xl"):
bg.link("← Back to Posts", "/posts").classes("q-btn q-btn-outline")
create_footer()
# About Page
with bg.page("/about", "AboutPage", "About"):
create_header()
with bg.column().classes("q-pa-xl max-width-md mx-auto text-center"):
bg.label("About Me").classes("text-h3 q-mb-lg")
bg.label("Welcome to my personal blog...").classes("text-body1 q-mb-lg")
bg.label("I write about technology, life, and everything in between.").classes("text-body1 q-mb-lg")
create_footer()
# Contact Page
with bg.page("/contact", "ContactPage", "Contact"):
create_header()
with bg.column().classes("q-pa-xl max-width-sm mx-auto"):
bg.label("Contact Me").classes("text-h3 q-mb-lg text-center")
# Contact form
bg.input(placeholder="Your Name").classes("q-mb-md").props("filled required")
bg.input(placeholder="Your Email").classes("q-mb-md").props("filled required type=email")
bg.input(placeholder="Subject").classes("q-mb-md").props("filled required")
bg.input(placeholder="Your Message").classes("q-mb-lg").props("filled required type=textarea rows=4")
bg.button("Send Message").classes("q-btn q-btn-primary full-width")
create_footer()
# Build the application
bg.build("blog-app")
Route Configuration
-------------------
Generated Router Files
~~~~~~~~~~~~~~~~~~~~~~~
BadGUI automatically generates Vue Router configuration files:
**``src/router/routes.js``:**
.. code-block:: javascript
const routes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/HomePage.vue') },
{ path: '/about', component: () => import('pages/AboutPage.vue') },
{ path: '/posts', component: () => import('pages/PostsPage.vue') },
{ path: '/post/:id', component: () => import('pages/PostPage.vue') },
{ path: '/contact', component: () => import('pages/ContactPage.vue') }
]
}
]
**``src/router/index.js``:**
.. code-block:: javascript
import { createRouter, createWebHistory } from 'vue-router'
import routes from './routes'
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
Page Components
~~~~~~~~~~~~~~~
Each page generates a corresponding Vue component:
**``src/pages/HomePage.vue``:**
.. code-block:: vue
<template>
<q-page class="row items-center justify-evenly">
<!-- Generated components -->
</q-page>
</template>
<script setup>
import { ref } from 'vue'
// Component logic
</script>
Best Practices
--------------
URL Structure
~~~~~~~~~~~~~
1. **Use clear, descriptive URLs**: ``/about``, ``/contact``, ``/blog/post-title``
2. **Keep URLs short and readable**: Avoid deep nesting
3. **Use hyphens for multi-word URLs**: ``/user-profile`` not ``/userprofile``
4. **Be consistent**: Choose a pattern and stick to it
Page Organization
~~~~~~~~~~~~~~~~~
1. **Group related pages**: Keep similar functionality together
2. **Use consistent layouts**: Share common elements like headers/footers
3. **Plan your navigation flow**: Make it easy for users to move between pages
4. **Consider SEO**: Use descriptive page titles
Performance Considerations
~~~~~~~~~~~~~~~~~~~~~~~~~~
1. **Code splitting**: Vue Router automatically splits pages into separate chunks
2. **Lazy loading**: Pages are loaded only when needed
3. **Minimize page complexity**: Keep individual pages focused and lightweight
Error Handling
--------------
404 Error Page
~~~~~~~~~~~~~~
BadGUI automatically generates a 404 error page:
.. code-block:: python
# This is generated automatically
with bg.page("/:catchAll(.*)*", "ErrorNotFound", "Page Not Found"):
bg.label("Oops. Nothing here...").classes("text-h2")
bg.link("Go Home", "/").classes("q-btn q-btn-primary")
The generated project includes proper error handling for routes that don't exist.
Advanced Routing
----------------
Route Guards
~~~~~~~~~~~~
While BadGUI generates static routes, you can add route guards in the generated Vue project:
.. code-block:: javascript
// In the generated router/index.js, you can add:
router.beforeEach((to, from, next) => {
// Add authentication, analytics, etc.
next()
})
Programmatic Navigation
~~~~~~~~~~~~~~~~~~~~~~~
In the generated Vue components, you can add programmatic navigation:
.. code-block:: vue
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const navigateToAbout = () => {
router.push('/about')
}
</script>
This allows for dynamic navigation based on user actions or application state.