Router API
The Router module provides routing functionality for Single-Page Applications (SPAs).
Import
import { Router } from "winnetoujs/modules/router";
Static Methods
Router.navigate()
Navigates to a specified route.
Signature:
Router.navigate(route: string): void
Parameters:
route- The route path to navigate to (e.g.,"/home","/users/123")
Returns: void
Usage:
Router.navigate("/home");
Router.navigate("/about");
Router.navigate("/users/profile");
Features:
- Updates browser history
- Triggers route handler function
- Calls
onGolifecycle hook - Supports browser back/forward buttons
Router.createRoutes()
Registers all routes and lifecycle hooks.
Signature:
Router.createRoutes(
routes: Record<string, Function>,
hooks?: RouterHooks
): void
Parameters:
routes- Object mapping route paths to handler functionshooks- Optional lifecycle hooks objectonGo?: (route: string) => void- Called after navigationonBack?: (route: string) => void- Called after back navigation
Returns: void
Usage:
const routes = {
"/home": () => {
console.log("Home route");
loadHomePage();
},
"/about": () => {
console.log("About route");
loadAboutPage();
},
"/settings": () => {
console.log("Settings route");
loadSettingsPage();
},
};
Router.createRoutes(routes, {
onGo(route) {
console.log("Navigated to:", route);
updateActiveMenu(route);
},
onBack(route) {
console.log("Went back to:", route);
updateBreadcrumbs(route);
},
});
Router Class Pattern
The recommended way to organize routing is using a dedicated router class.
Basic Router Implementation
import { Router } from "winnetoujs/modules/router";
export class MyRouter {
constructor() {
this.createRoutes();
}
private routes = {};
public methods = {
home: {
go: () => Router.navigate("/home"),
set: () => {
this.routes["/home"] = () => {
console.log("Home route called");
// Your route logic here
};
},
},
about: {
go: () => Router.navigate("/about"),
set: () => {
this.routes["/about"] = () => {
console.log("About route called");
// Your route logic here
};
},
},
settings: {
go: () => Router.navigate("/settings"),
set: () => {
this.routes["/settings"] = () => {
console.log("Settings route called");
// Your route logic here
};
},
},
};
private createRoutes() {
// Register all routes
Object.keys(this.methods).forEach(key => {
this.methods[key].set();
});
// Create the router with lifecycle hooks
Router.createRoutes(this.routes, {
onGo(route) {
console.log("Navigating to:", route);
},
onBack(route) {
console.log("Going back to:", route);
},
});
}
}
Using the Router
import { W } from "winnetoujs";
import { $navButton } from "./components.wcto";
import { MyRouter } from "./router";
// Initialize router
const router = new MyRouter();
// Navigate programmatically
router.methods.home.go();
// Navigate from constructo
new $navButton({
text: "Home",
onclick: W.fx(() => {
router.methods.home.go();
}),
}).create("#nav");
new $navButton({
text: "About",
onclick: W.fx(() => {
router.methods.about.go();
}),
}).create("#nav");
Route Methods Structure
Each route in the methods object has two functions:
go()
Triggers navigation to the route.
Signature:
go: () => void
Usage:
public methods = {
home: {
go: () => Router.navigate("/home"),
// ...
}
};
// Call to navigate
router.methods.home.go();
set()
Defines the route handler logic.
Signature:
set: () => void
Usage:
public methods = {
home: {
// ...
set: () => {
this.routes["/home"] = () => {
// Route-specific logic
loadHomePage();
updateTitle("Home");
trackPageView("/home");
};
},
}
};
Lifecycle Hooks
onGo()
Called after navigating to a new route.
Signature:
onGo: (route: string) => void
Parameters:
route- The route that was navigated to
Usage:
Router.createRoutes(routes, {
onGo(route) {
console.log("Navigated to:", route);
// Update page title
document.title = `App - ${route}`;
// Track analytics
analytics.pageView(route);
// Update active menu
updateActiveMenuItem(route);
// Scroll to top
window.scrollTo(0, 0);
},
});
onBack()
Called after using browser back button.
Signature:
onBack: (route: string) => void
Parameters:
route- The route that was navigated back to
Usage:
Router.createRoutes(routes, {
onBack(route) {
console.log("Went back to:", route);
// Restore scroll position
restoreScrollPosition(route);
// Update breadcrumbs
updateBreadcrumbs(route);
// Log navigation
console.log("User navigated back to:", route);
},
});
Best Practices
Route Organization
Organize routes by feature:
export class AppRouter {
public methods = {
// Auth routes
login: {
go: () => Router.navigate("/login"),
set: () => { this.routes["/login"] = () => loadLogin(); },
},
register: {
go: () => Router.navigate("/register"),
set: () => { this.routes["/register"] = () => loadRegister(); },
},
// User routes
profile: {
go: () => Router.navigate("/profile"),
set: () => { this.routes["/profile"] = () => loadProfile(); },
},
settings: {
go: () => Router.navigate("/settings"),
set: () => { this.routes["/settings"] = () => loadSettings(); },
},
// Content routes
home: {
go: () => Router.navigate("/"),
set: () => { this.routes["/"] = () => loadHome(); },
},
about: {
go: () => Router.navigate("/about"),
set: () => { this.routes["/about"] = () => loadAbout(); },
},
};
}
Route Handlers
Keep route handlers focused:
// ✅ Good - Clean and focused
set: () => {
this.routes["/products"] = () => {
this.loadProductsPage();
};
};
// ❌ Avoid - Too much logic
set: () => {
this.routes["/products"] = () => {
// 100 lines of code...
};
};
Extract complex logic to methods:
export class AppRouter {
private routes = {};
public methods = {
products: {
go: () => Router.navigate("/products"),
set: () => {
this.routes["/products"] = () => this.loadProducts();
},
},
};
private loadProducts() {
this.clearContent();
this.showLoader();
fetch("/api/products")
.then(data => this.renderProducts(data))
.finally(() => this.hideLoader());
}
private renderProducts(data) {
// Rendering logic
}
}
Lifecycle Hooks
Use hooks for global behavior:
Router.createRoutes(this.routes, {
onGo(route) {
// Global actions on navigation
this.updateBreadcrumbs(route);
this.trackAnalytics(route);
this.closeModals();
window.scrollTo(0, 0);
},
onBack(route) {
// Global actions on back navigation
this.restoreState(route);
this.updateHistory(route);
},
});
Common Patterns
Protected Routes
export class AppRouter {
private isAuthenticated() {
return W.getMutable("userToken") !== null;
}
public methods = {
dashboard: {
go: () => Router.navigate("/dashboard"),
set: () => {
this.routes["/dashboard"] = () => {
if (!this.isAuthenticated()) {
this.methods.login.go();
return;
}
loadDashboard();
};
},
},
login: {
go: () => Router.navigate("/login"),
set: () => {
this.routes["/login"] = () => loadLogin();
},
},
};
}
Route Parameters
export class AppRouter {
public methods = {
userProfile: {
go: (userId) => Router.navigate(`/user/${userId}`),
set: () => {
this.routes["/user/:id"] = () => {
const userId = this.getRouteParam("id");
loadUserProfile(userId);
};
},
},
};
private getRouteParam(param) {
const path = window.location.pathname;
// Parse parameter from path
return path.split("/").pop();
}
}
// Usage
router.methods.userProfile.go("123");
Nested Navigation
export class AppRouter {
public methods = {
products: {
go: () => Router.navigate("/products"),
set: () => {
this.routes["/products"] = () => {
this.loadProductsLayout();
this.methods.productsList.go();
};
},
},
productsList: {
go: () => Router.navigate("/products/list"),
set: () => {
this.routes["/products/list"] = () => {
this.loadProductsList();
};
},
},
productDetail: {
go: (id) => Router.navigate(`/products/${id}`),
set: () => {
this.routes["/products/:id"] = () => {
const productId = this.getProductId();
this.loadProductDetail(productId);
};
},
},
};
}
Breadcrumbs Management
Router.createRoutes(this.routes, {
onGo(route) {
const breadcrumbs = this.generateBreadcrumbs(route);
this.updateBreadcrumbsUI(breadcrumbs);
},
});
private generateBreadcrumbs(route) {
const parts = route.split("/").filter(Boolean);
return parts.map((part, index) => ({
label: this.formatLabel(part),
path: "/" + parts.slice(0, index + 1).join("/"),
}));
}
Page Transitions
Router.createRoutes(this.routes, {
onGo(route) {
// Fade out
document.querySelector("#app").classList.add("fade-out");
setTimeout(() => {
// Load content
this.loadRoute(route);
// Fade in
document.querySelector("#app").classList.remove("fade-out");
document.querySelector("#app").classList.add("fade-in");
}, 300);
},
});