jac-client 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jac_client/docs/README.md +629 -0
- jac_client/docs/advanced-state.md +706 -0
- jac_client/docs/imports.md +650 -0
- jac_client/docs/lifecycle-hooks.md +554 -0
- jac_client/docs/routing.md +530 -0
- jac_client/examples/little-x/app.jac +615 -0
- jac_client/examples/little-x/package-lock.json +2840 -0
- jac_client/examples/little-x/package.json +23 -0
- jac_client/examples/little-x/submit-button.jac +8 -0
- jac_client/examples/todo-app/README.md +82 -0
- jac_client/examples/todo-app/app.jac +683 -0
- jac_client/examples/todo-app/package-lock.json +999 -0
- jac_client/examples/todo-app/package.json +22 -0
- jac_client/plugin/cli.py +328 -0
- jac_client/plugin/client.py +41 -0
- jac_client/plugin/client_runtime.jac +941 -0
- jac_client/plugin/vite_client_bundle.py +470 -0
- jac_client/tests/__init__.py +2 -0
- jac_client/tests/fixtures/button.jac +6 -0
- jac_client/tests/fixtures/client_app.jac +18 -0
- jac_client/tests/fixtures/client_app_with_antd.jac +21 -0
- jac_client/tests/fixtures/js_import.jac +30 -0
- jac_client/tests/fixtures/package-lock.json +329 -0
- jac_client/tests/fixtures/package.json +11 -0
- jac_client/tests/fixtures/relative_import.jac +13 -0
- jac_client/tests/fixtures/test_fragments_spread.jac +44 -0
- jac_client/tests/fixtures/utils.js +22 -0
- jac_client/tests/test_cl.py +360 -0
- jac_client/tests/test_create_jac_app.py +139 -0
- jac_client-0.1.0.dist-info/METADATA +126 -0
- jac_client-0.1.0.dist-info/RECORD +33 -0
- jac_client-0.1.0.dist-info/WHEEL +4 -0
- jac_client-0.1.0.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
# Routing in Jac: Building Multi-Page Applications
|
|
2
|
+
|
|
3
|
+
Learn how to use `initRouter()` to create multi-page applications with navigation, route guards, and dynamic routing.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📚 Table of Contents
|
|
8
|
+
|
|
9
|
+
- [What is Routing?](#what-is-routing)
|
|
10
|
+
- [Basic Routing Setup](#basic-routing-setup)
|
|
11
|
+
- [Route Configuration](#route-configuration)
|
|
12
|
+
- [Navigation](#navigation)
|
|
13
|
+
- [Route Guards](#route-guards)
|
|
14
|
+
- [Complete Example](#complete-example)
|
|
15
|
+
- [Advanced Patterns](#advanced-patterns)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What is Routing?
|
|
20
|
+
|
|
21
|
+
Routing allows you to create multi-page applications where different URLs display different components. In Jac, routing is handled by the `initRouter()` function, which manages navigation using hash-based routing (e.g., `#/login`, `#/todos`).
|
|
22
|
+
|
|
23
|
+
**Key Benefits:**
|
|
24
|
+
- **Single Page Application (SPA)**: No page refreshes when navigating
|
|
25
|
+
- **URL-based Navigation**: Each view has its own URL
|
|
26
|
+
- **Browser History**: Back/forward buttons work automatically
|
|
27
|
+
- **Route Guards**: Protect routes with authentication checks
|
|
28
|
+
- **Reactive Updates**: Route changes automatically update components
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Basic Routing Setup
|
|
33
|
+
|
|
34
|
+
### Setting Up the Router
|
|
35
|
+
|
|
36
|
+
```jac
|
|
37
|
+
cl {
|
|
38
|
+
def App() -> any {
|
|
39
|
+
# Define routes
|
|
40
|
+
routes = [
|
|
41
|
+
{"path": "/", "component": lambda -> any { return HomeView(); }, "guard": None},
|
|
42
|
+
{"path": "/about", "component": lambda -> any { return AboutView(); }, "guard": None}
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
# Initialize router with default route
|
|
46
|
+
router = initRouter(routes, "/");
|
|
47
|
+
|
|
48
|
+
# Render the router
|
|
49
|
+
return <div>
|
|
50
|
+
{router.render()}
|
|
51
|
+
</div>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
def jac_app() -> any {
|
|
55
|
+
return App();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Key Components:**
|
|
61
|
+
- **`routes`**: Array of route configurations
|
|
62
|
+
- **`initRouter()`**: Creates the router instance
|
|
63
|
+
- **`router.render()`**: Renders the component for the current route
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Route Configuration
|
|
68
|
+
|
|
69
|
+
Each route is a dictionary with three properties:
|
|
70
|
+
|
|
71
|
+
### Route Structure
|
|
72
|
+
|
|
73
|
+
```jac
|
|
74
|
+
route = {
|
|
75
|
+
"path": "/todos", # URL path
|
|
76
|
+
"component": lambda -> any { # Component to render
|
|
77
|
+
return TodoApp();
|
|
78
|
+
},
|
|
79
|
+
"guard": jacIsLoggedIn # Optional: route guard function
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Route Properties
|
|
84
|
+
|
|
85
|
+
| Property | Type | Required | Description |
|
|
86
|
+
|----------|------|----------|-------------|
|
|
87
|
+
| `path` | `str` | ✅ Yes | URL path (must start with `/`) |
|
|
88
|
+
| `component` | `function` | ✅ Yes | Function that returns a JSX component |
|
|
89
|
+
| `guard` | `function` | ❌ No | Function that returns `True` if route is accessible |
|
|
90
|
+
|
|
91
|
+
### Example: Multiple Routes
|
|
92
|
+
|
|
93
|
+
```jac
|
|
94
|
+
cl {
|
|
95
|
+
def App() -> any {
|
|
96
|
+
# Define all routes
|
|
97
|
+
home_route = {
|
|
98
|
+
"path": "/",
|
|
99
|
+
"component": lambda -> any { return HomeView(); },
|
|
100
|
+
"guard": None
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
login_route = {
|
|
104
|
+
"path": "/login",
|
|
105
|
+
"component": lambda -> any { return LoginForm(); },
|
|
106
|
+
"guard": None
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
todos_route = {
|
|
110
|
+
"path": "/todos",
|
|
111
|
+
"component": lambda -> any { return TodoApp(); },
|
|
112
|
+
"guard": jacIsLoggedIn # Requires authentication
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
profile_route = {
|
|
116
|
+
"path": "/profile",
|
|
117
|
+
"component": lambda -> any { return ProfileView(); },
|
|
118
|
+
"guard": jacIsLoggedIn # Requires authentication
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
# Combine all routes
|
|
122
|
+
routes = [home_route, login_route, todos_route, profile_route];
|
|
123
|
+
|
|
124
|
+
# Initialize router with default route
|
|
125
|
+
router = initRouter(routes, "/");
|
|
126
|
+
|
|
127
|
+
return <div>
|
|
128
|
+
{Nav(router.path())}
|
|
129
|
+
{router.render()}
|
|
130
|
+
</div>;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Navigation
|
|
138
|
+
|
|
139
|
+
Jac provides multiple ways to navigate between routes.
|
|
140
|
+
|
|
141
|
+
### Using the `navigate()` Runtime Function
|
|
142
|
+
|
|
143
|
+
```jac
|
|
144
|
+
cl {
|
|
145
|
+
async def handleLogin(e: any) -> None {
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
success = await jacLogin(username, password);
|
|
148
|
+
if success {
|
|
149
|
+
navigate("/todos"); # Global navigate function
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Using the `Link` Component
|
|
156
|
+
|
|
157
|
+
The `Link` component creates clickable links that navigate to routes:
|
|
158
|
+
|
|
159
|
+
```jac
|
|
160
|
+
cl {
|
|
161
|
+
def Nav() -> any {
|
|
162
|
+
return <nav>
|
|
163
|
+
<Link href="/">Home</Link>
|
|
164
|
+
<Link href="/todos">Todos</Link>
|
|
165
|
+
<Link href="/profile">Profile</Link>
|
|
166
|
+
</nav>;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Link Component Features:**
|
|
172
|
+
- Automatic hash-based navigation
|
|
173
|
+
- Updates URL without page refresh
|
|
174
|
+
- Triggers route changes and component updates
|
|
175
|
+
|
|
176
|
+
### Complete Navigation Example
|
|
177
|
+
|
|
178
|
+
```jac
|
|
179
|
+
cl {
|
|
180
|
+
def Nav(currentPath: str) -> any {
|
|
181
|
+
return <nav style={{
|
|
182
|
+
"display": "flex",
|
|
183
|
+
"gap": "16px",
|
|
184
|
+
"padding": "12px"
|
|
185
|
+
}}>
|
|
186
|
+
<Link href="/" style={{
|
|
187
|
+
"color": ("#7C3AED" if currentPath == "/" else "#111827"),
|
|
188
|
+
"textDecoration": "none",
|
|
189
|
+
"fontWeight": ("700" if currentPath == "/" else "500")
|
|
190
|
+
}}>
|
|
191
|
+
Home
|
|
192
|
+
</Link>
|
|
193
|
+
<Link href="/todos" style={{
|
|
194
|
+
"color": ("#7C3AED" if currentPath == "/todos" else "#111827"),
|
|
195
|
+
"textDecoration": "none",
|
|
196
|
+
"fontWeight": ("700" if currentPath == "/todos" else "500")
|
|
197
|
+
}}>
|
|
198
|
+
Todos
|
|
199
|
+
</Link>
|
|
200
|
+
<button onClick={lambda -> None {
|
|
201
|
+
navigate("/login");
|
|
202
|
+
}}>
|
|
203
|
+
Logout
|
|
204
|
+
</button>
|
|
205
|
+
</nav>;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
def App() -> any {
|
|
209
|
+
routes = [/* routes */];
|
|
210
|
+
router = initRouter(routes, "/");
|
|
211
|
+
currentPath = router.path();
|
|
212
|
+
|
|
213
|
+
return <div>
|
|
214
|
+
{Nav(currentPath)}
|
|
215
|
+
{router.render()}
|
|
216
|
+
</div>;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Route Guards
|
|
224
|
+
|
|
225
|
+
Route guards protect routes by checking conditions (like authentication) before allowing access.
|
|
226
|
+
|
|
227
|
+
### How Guards Work
|
|
228
|
+
|
|
229
|
+
1. **Guard Function**: A function that returns `True` (allow) or `False` (deny)
|
|
230
|
+
2. **Automatic Check**: Router checks the guard when the route matches
|
|
231
|
+
3. **Access Denied**: If guard returns `False`, shows "Access Denied" instead of the component
|
|
232
|
+
|
|
233
|
+
### Basic Guard Example
|
|
234
|
+
|
|
235
|
+
```jac
|
|
236
|
+
cl {
|
|
237
|
+
def App() -> any {
|
|
238
|
+
public_route = {
|
|
239
|
+
"path": "/public",
|
|
240
|
+
"component": lambda -> any { return PublicView(); },
|
|
241
|
+
"guard": None # No guard - always accessible
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
protected_route = {
|
|
245
|
+
"path": "/private",
|
|
246
|
+
"component": lambda -> any { return PrivateView(); },
|
|
247
|
+
"guard": jacIsLoggedIn # Requires authentication
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
routes = [public_route, protected_route];
|
|
251
|
+
router = initRouter(routes, "/");
|
|
252
|
+
|
|
253
|
+
return <div>{router.render()}</div>;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Custom Guard Functions
|
|
259
|
+
|
|
260
|
+
You can create custom guard functions:
|
|
261
|
+
|
|
262
|
+
```jac
|
|
263
|
+
cl {
|
|
264
|
+
# Check if user is admin
|
|
265
|
+
def isAdmin() -> bool {
|
|
266
|
+
if not jacIsLoggedIn() {
|
|
267
|
+
return False;
|
|
268
|
+
}
|
|
269
|
+
user = getCurrentUser(); # Your custom function
|
|
270
|
+
return user.role == "admin";
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# Check if user has specific permission
|
|
274
|
+
def hasPermission(permission: str) -> bool {
|
|
275
|
+
if not jacIsLoggedIn() {
|
|
276
|
+
return False;
|
|
277
|
+
}
|
|
278
|
+
user = getCurrentUser();
|
|
279
|
+
return permission in user.permissions;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
def App() -> any {
|
|
283
|
+
admin_route = {
|
|
284
|
+
"path": "/admin",
|
|
285
|
+
"component": lambda -> any { return AdminView(); },
|
|
286
|
+
"guard": isAdmin # Custom guard
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
settings_route = {
|
|
290
|
+
"path": "/settings",
|
|
291
|
+
"component": lambda -> any { return SettingsView(); },
|
|
292
|
+
"guard": lambda -> bool { return hasPermission("settings:edit"); } # Inline guard
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
routes = [admin_route, settings_route];
|
|
296
|
+
router = initRouter(routes, "/");
|
|
297
|
+
|
|
298
|
+
return <div>{router.render()}</div>;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Redirecting on Guard Failure
|
|
304
|
+
|
|
305
|
+
You can automatically redirect when a guard fails:
|
|
306
|
+
|
|
307
|
+
```jac
|
|
308
|
+
cl {
|
|
309
|
+
def protectedGuard() -> bool {
|
|
310
|
+
if not jacIsLoggedIn() {
|
|
311
|
+
navigate("/login"); # Redirect to login
|
|
312
|
+
return False;
|
|
313
|
+
}
|
|
314
|
+
return True;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
def App() -> any {
|
|
318
|
+
protected_route = {
|
|
319
|
+
"path": "/todos",
|
|
320
|
+
"component": lambda -> any { return TodoApp(); },
|
|
321
|
+
"guard": protectedGuard
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
routes = [protected_route];
|
|
325
|
+
router = initRouter(routes, "/login");
|
|
326
|
+
|
|
327
|
+
return <div>{router.render()}</div>;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Complete Example
|
|
335
|
+
|
|
336
|
+
Here's a complete routing example from the Todo App:
|
|
337
|
+
|
|
338
|
+
```jac
|
|
339
|
+
cl {
|
|
340
|
+
def Nav(route: str) -> any {
|
|
341
|
+
if not jacIsLoggedIn() or route == "/login" or route == "/signup" {
|
|
342
|
+
return None;
|
|
343
|
+
}
|
|
344
|
+
return <nav style={{
|
|
345
|
+
"background": "#FFFFFF",
|
|
346
|
+
"padding": "12px",
|
|
347
|
+
"boxShadow": "0 1px 2px rgba(17,24,39,0.06)"
|
|
348
|
+
}}>
|
|
349
|
+
<div style={{
|
|
350
|
+
"maxWidth": "960px",
|
|
351
|
+
"margin": "0 auto",
|
|
352
|
+
"display": "flex",
|
|
353
|
+
"gap": "16px",
|
|
354
|
+
"alignItems": "center"
|
|
355
|
+
}}>
|
|
356
|
+
<Link href="/todos" style={{"textDecoration": "none"}}>
|
|
357
|
+
<span style={{
|
|
358
|
+
"color": "#111827",
|
|
359
|
+
"fontWeight": "800",
|
|
360
|
+
"fontSize": "18px"
|
|
361
|
+
}}>📝 My Todos</span>
|
|
362
|
+
</Link>
|
|
363
|
+
<button
|
|
364
|
+
onClick={logout_action}
|
|
365
|
+
style={{
|
|
366
|
+
"marginLeft": "auto",
|
|
367
|
+
"padding": "8px 12px",
|
|
368
|
+
"background": "#FFFFFF",
|
|
369
|
+
"color": "#374151",
|
|
370
|
+
"border": "1px solid #E5E7EB",
|
|
371
|
+
"borderRadius": "18px",
|
|
372
|
+
"cursor": "pointer"
|
|
373
|
+
}}
|
|
374
|
+
>
|
|
375
|
+
Logout
|
|
376
|
+
</button>
|
|
377
|
+
</div>
|
|
378
|
+
</nav>;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
def App() -> any {
|
|
382
|
+
# Define routes
|
|
383
|
+
login_route = {
|
|
384
|
+
"path": "/login",
|
|
385
|
+
"component": lambda -> any { return LoginForm(); },
|
|
386
|
+
"guard": None
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
signup_route = {
|
|
390
|
+
"path": "/signup",
|
|
391
|
+
"component": lambda -> any { return SignupForm(); },
|
|
392
|
+
"guard": None
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
todos_route = {
|
|
396
|
+
"path": "/todos",
|
|
397
|
+
"component": lambda -> any { return TodoApp(); },
|
|
398
|
+
"guard": jacIsLoggedIn # Protected route
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
# Initialize router
|
|
402
|
+
routes = [login_route, signup_route, todos_route];
|
|
403
|
+
router = initRouter(routes, "/login"); # Default to login page
|
|
404
|
+
|
|
405
|
+
# Get current path for navigation
|
|
406
|
+
currentPath = router.path();
|
|
407
|
+
|
|
408
|
+
return <div style={{
|
|
409
|
+
"minHeight": "95vh",
|
|
410
|
+
"background": "#F7F8FA",
|
|
411
|
+
"padding": "24px"
|
|
412
|
+
}}>
|
|
413
|
+
{Nav(currentPath)}
|
|
414
|
+
<div style={{
|
|
415
|
+
"maxWidth": "960px",
|
|
416
|
+
"margin": "0 auto",
|
|
417
|
+
"padding": "20px"
|
|
418
|
+
}}>
|
|
419
|
+
{router.render()}
|
|
420
|
+
</div>
|
|
421
|
+
</div>;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
def jac_app() -> any {
|
|
425
|
+
return App();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Advanced Patterns
|
|
433
|
+
|
|
434
|
+
### Getting Current Route
|
|
435
|
+
|
|
436
|
+
Use `router.path()` to get the current route:
|
|
437
|
+
|
|
438
|
+
```jac
|
|
439
|
+
cl {
|
|
440
|
+
def App() -> any {
|
|
441
|
+
routes = [/* routes */];
|
|
442
|
+
router = initRouter(routes, "/");
|
|
443
|
+
|
|
444
|
+
currentPath = router.path(); # Get current route
|
|
445
|
+
|
|
446
|
+
# Use currentPath for conditional rendering
|
|
447
|
+
return <div>
|
|
448
|
+
{Nav(currentPath)}
|
|
449
|
+
{router.render()}
|
|
450
|
+
</div>;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Programmatic Navigation
|
|
456
|
+
|
|
457
|
+
Navigate programmatically from anywhere:
|
|
458
|
+
|
|
459
|
+
```jac
|
|
460
|
+
cl {
|
|
461
|
+
async def handleLogin(e: any) -> None {
|
|
462
|
+
e.preventDefault();
|
|
463
|
+
success = await jacLogin(username, password);
|
|
464
|
+
if success {
|
|
465
|
+
router = __jacReactiveContext.router;
|
|
466
|
+
if router {
|
|
467
|
+
router.navigate("/todos");
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
def logout_action() -> None {
|
|
473
|
+
jacLogout();
|
|
474
|
+
navigate("/login"); # Or use router.navigate()
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Dynamic Route Parameters (Future)
|
|
480
|
+
|
|
481
|
+
While Jac's router uses hash-based routing without path parameters, you can pass state through navigation:
|
|
482
|
+
|
|
483
|
+
```jac
|
|
484
|
+
cl {
|
|
485
|
+
def navigateToTodo(id: str) -> None {
|
|
486
|
+
# Store ID in state before navigating
|
|
487
|
+
setTodoId(id);
|
|
488
|
+
navigate("/todo-detail");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
def TodoDetailView() -> any {
|
|
492
|
+
id = todoId();
|
|
493
|
+
todo = getTodoById(id); # Fetch todo by ID
|
|
494
|
+
return <div>{todo.text}</div>;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### 404 Handling
|
|
500
|
+
|
|
501
|
+
The router automatically shows a 404 message if no route matches:
|
|
502
|
+
|
|
503
|
+
```jac
|
|
504
|
+
# If user navigates to /unknown-route
|
|
505
|
+
# Router automatically renders: "404 - Route not found: /unknown-route"
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## Best Practices
|
|
511
|
+
|
|
512
|
+
1. **Default Route**: Always provide a sensible default route in `initRouter()`
|
|
513
|
+
2. **Route Guards**: Use guards for protected routes instead of checking in components
|
|
514
|
+
3. **Link Components**: Use `Link` component instead of manual hash manipulation
|
|
515
|
+
4. **Guard Functions**: Keep guard functions simple and focused
|
|
516
|
+
5. **Navigation**: Use `navigate()` for programmatic navigation
|
|
517
|
+
6. **Current Path**: Use `router.path()` for conditional rendering based on current route
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Summary
|
|
522
|
+
|
|
523
|
+
- **Routing**: Use `initRouter()` to create multi-page applications
|
|
524
|
+
- **Routes**: Configure routes with `path`, `component`, and optional `guard`
|
|
525
|
+
- **Navigation**: Use `Link` component or `navigate()` function
|
|
526
|
+
- **Guards**: Protect routes with guard functions
|
|
527
|
+
- **Current Route**: Access current route with `router.path()`
|
|
528
|
+
|
|
529
|
+
Routing in Jac is simple, reactive, and powerful! 🚀
|
|
530
|
+
|