Component Preloading & Multi-Section Layouts¶
New in v2.0.4
Component preloading for building complex, multi-section layouts
🚀 Overview¶
PhpSPA v2.0.4 introduces powerful features for building complex layouts where different sections update independently. Perfect for messaging apps, dashboards, and multi-panel interfaces.
Key Features:
name()- Assign unique identifiers to componentspreload()- Load multiple components simultaneously on different target IDsexact()- Revert to default content when navigating awaypattern()- Match routes using fnmatch patterns (e.g.,/blog/*)method()- Accept multiple HTTP methodsroute()- Define multiple routes
📱 Real-World Example: WhatsApp Web Clone¶
Build a messaging interface with independent user list and chat sections.
Layout¶
<?php
function Layout() {
return <<<HTML
<div class="container">
<section id="users"></section>
<section id="chat">
<div class="welcome">Welcome! Select a user to start chatting.</div>
</section>
</div>
HTML;
}
Components¶
Step 1: Create the User List Component¶
This component shows on the homepage and displays all available users to chat with.
<?php
use PhpSPA\Component;
$userList = (new Component(function() {
$users = [
['id' => 1, 'name' => 'John Doe'],
['id' => 2, 'name' => 'Jane Smith'],
];
$html = '<div class="user-list"><h3>Chats</h3>';
foreach ($users as $user) {
$html .= "<a href='/chat/{$user['id']}'>{$user['name']}</a>";
}
return $html . '</div>';
}))
->route('/') // Shows on homepage
->targetID('users') // Renders in id="users"
->name('userList'); // Named for preloading
Key points:
route('/')- This component renders on the homepagetargetID('users')- Content goes into the<section id="users">elementname('userList')- Assigns a unique name so other components can reference it
Step 2: Create the Chat Component¶
This component displays messages for a specific user. The magic happens with preload() and exact().
<?php
$chatView = (new Component(function(array $path) {
$userId = $path['id'];
$messages = getMessages($userId); // Your database function
$html = "<div class='chat-header'>Chat with User {$userId}</div>";
foreach ($messages as $msg) {
$html .= "<div class='message'>{$msg['text']}</div>";
}
return $html;
}))
->route('/chat/{id: int}') // Matches /chat/1, /chat/2, etc.
->targetID('chat') // Renders in id="chat"
->preload('userList') // CRITICAL: Also load user list
->exact(); // Revert to welcome when navigating away
Key points:
route('/chat/{id: int}')- Matches/chat/1,/chat/2, etc. with typed parametertargetID('chat')- Content goes into the<section id="chat">elementpreload('userList')- This is the magic! Also loads the user list componentexact()- When navigating away from this route, revert to the default welcome message
Step 3: Register Components & Run¶
What Happens¶
On / route:
┌───────────────┬───────────────────┐
│ User List │ Welcome Message │
│ • John Doe │ Select a user │
│ • Jane Smith │ to start... │
└───────────────┴───────────────────┘
On /chat/1 route:
┌───────────────┬───────────────────┐
│ User List │ Chat with John │
│ • John Doe │ John: Hey! │
│ • Jane Smith │ You: Hi! │
└───────────────┴───────────────────┘
Both sections render because of preload()
Navigate back to /:
┌───────────────┬───────────────────┐
│ User List │ Welcome Message │
│ • John Doe │ Select a user │
│ • Jane Smith │ to start... │
└───────────────┴───────────────────┘
Chat reverts to welcome because of exact()
⚙️ Method Reference¶
name(string $value)¶
Assigns unique identifier for preloading reference.
preload(string ...$componentNames)¶
Loads additional named components together.
exact()¶
Reverts to default content when route doesn't match.
<?php
$profile = (new Component(...))
->route('/profile/{id: int}')
->exact(); // Reverts to default when navigating away
Without exact(): Stale content remains visible
With exact(): Clean revert to default content
pattern()¶
Enables fnmatch pattern matching.
| Pattern | Matches |
|---|---|
/blog/* | /blog/my-post, /blog/123 |
/user/*/posts/* | /user/123/posts/456 |
Extracting Values from Patterns
Pattern matching only validates routes. To extract values, use the Request object:
method(string ...$methods)¶
Accepts multiple HTTP methods.
route(string ...$routes)¶
Defines multiple routes to same component.
🎨 More Examples¶
Multi-Section Dashboard¶
<?php
$navbar = (new Component(...))->name('navbar')->targetID('navbar');
$sidebar = (new Component(...))->name('sidebar')->targetID('sidebar');
$dashboard = (new Component(...))
->route('/dashboard')
->targetID('content')
->preload('navbar', 'sidebar');
$settings = (new Component(...))
->route('/dashboard/settings')
->targetID('content')
->preload('navbar', 'sidebar');
Email Client¶
<?php
$folders = (new Component(...))->name('folders')->targetID('folders');
$emailList = (new Component(...))->name('emailList')->targetID('emailList');
$emailViewer = (new Component(function(array $path) {
return "<section>Email #{$path['id']}</section>";
}))
->route('/email/{id: int}')
->targetID('emailViewer')
->preload('folders', 'emailList')
->exact();
⚠️ Common Pitfalls¶
❌ Same targetID for Multiple Components¶
<?php
$c1 = (new Component(...))->targetID('app');
$c2 = (new Component(...))->targetID('app'); // Replaces c1!
Solution: Use different target IDs.
❌ Forgetting name() for Preload¶
<?php
$sidebar = (new Component(...))->targetID('sidebar');
$main = (new Component(...))->preload('sidebar'); // ERROR!
Solution: Set name('sidebar').
❌ Not Using exact() for Dynamic Content¶
Without exact(), navigating away leaves stale content visible.
📚 Related Documentation¶
Component Basics Advanced Routing State Management
Need help? Open an issue