Conditional Rendering
WildflowerJS provides two conditional rendering attributes: data-show toggles CSS visibility while keeping elements in the DOM, and data-render adds or removes elements entirely.
data-show toggles CSS visibility while preserving DOM structure, and data-render adds/removes elements from the DOM entirely.
Basic Conditional Rendering
Use data-show to conditionally show/hide elements:
<div data-component="visibility-demo">
<p class="text-muted">Demonstrates basic show/hide functionality and login state management.</p>
<!-- Control buttons -->
<div class="mb-4">
<button data-action="toggleVisibility" class="btn me-2"
data-bind-class="isVisible ? 'btn-warning' : 'btn-success'">
<span data-bind="isVisible ? 'Hide' : 'Show'"></span> Content
</button>
<button data-action="toggleUser" class="btn me-2"
data-bind-class="isLoggedIn ? 'btn-outline-danger' : 'btn-primary'">
<span data-bind="isLoggedIn ? 'Logout' : 'Login'"></span>
</button>
<button data-action="changeUser" class="btn btn-secondary btn-sm">
Change User
</button>
</div>
<!-- Basic data-show conditional content -->
<div data-show="isVisible" class="alert alert-info mb-3">
<h5>๐ Conditionally Visible Content</h5>
<p>This element is hidden with <code>display: none</code> when the condition is false.</p>
<small class="text-muted">Visibility controlled by: <code>data-show="isVisible"</code></small>
</div>
<!-- Login state management -->
<div class="row">
<div class="col-md-6">
<!-- Logged in user interface -->
<div data-show="isLoggedIn" class="card border-success">
<div class="card-header bg-success text-white">
<h5 class="mb-0">๐ Welcome Back!</h5>
</div>
<div class="card-body">
<p><strong>Name:</strong> <span data-bind="user.name"></span></p>
<p><strong>Role:</strong> <span data-bind="user.role" class="badge bg-primary"></span></p>
<p><strong>Last login:</strong> <span data-bind="user.lastLogin"></span></p>
<small class="text-muted">Controlled by: <code>data-show="isLoggedIn"</code></small>
</div>
</div>
</div>
<div class="col-md-6">
<!-- Login prompt interface -->
<div data-show="!isLoggedIn" class="card border-warning">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0">๐ Authentication Required</h5>
</div>
<div class="card-body">
<p>You need to be logged in to access personalized content.</p>
<button data-action="login" class="btn btn-primary">
Login Now
</button>
<br><small class="text-muted mt-2 d-block">Controlled by: <code>data-show="!isLoggedIn"</code></small>
</div>
</div>
</div>
</div>
<!-- Status display -->
<div class="mt-3 p-2 bg-light rounded">
<small>
<strong>Current State:</strong>
Content Visible: <span data-bind="isVisible ? 'Yes' : 'No'" class="badge bg-secondary"></span> |
User Logged In: <span data-bind="isLoggedIn ? 'Yes' : 'No'" class="badge bg-secondary"></span>
</small>
</div>
</div>
wildflower.component('visibility-demo', {
state: {
isVisible: true,
isLoggedIn: false,
user: {
name: 'John Doe',
role: 'Administrator',
lastLogin: '2024-01-15 09:30:00'
}
},
// Toggle content visibility
toggleVisibility() {
this.isVisible = !this.isVisible
},
// Toggle user login state
toggleUser() {
this.isLoggedIn = !this.isLoggedIn
// Update last login time when logging in
if (this.isLoggedIn) {
this.user.lastLogin = new Date().toLocaleString()
}
},
// Direct login action
login() {
this.isLoggedIn = true
this.user.lastLogin = new Date().toLocaleString()
},
// Change user demo
changeUser() {
const users = [
{ name: 'John Doe', role: 'Administrator' },
{ name: 'Jane Smith', role: 'Manager' },
{ name: 'Bob Wilson', role: 'Developer' },
{ name: 'Alice Johnson', role: 'Designer' }
]
const currentIndex = users.findIndex(u => u.name === this.user.name)
const nextIndex = (currentIndex + 1) % users.length
const newUser = users[nextIndex]
// Update user info while preserving login state
this.user.name = newUser.name
this.user.role = newUser.role
// Update last login time if logged in
if (this.isLoggedIn) {
this.user.lastLogin = new Date().toLocaleString()
}
}
})
Conditional Attributes
WildflowerJS provides two approaches to conditional rendering, each with different behavior and use cases:
| Attribute | Behavior | Performance | Use Case |
|---|---|---|---|
data-show |
Toggles display: none |
Fast toggle, element stays in DOM | Frequently toggled content, form inputs that need to preserve state |
data-render |
Adds/removes element from DOM | Slower toggle, reduces DOM size when hidden | Heavy content, infrequently shown, conditional component initialization |
data-show vs data-render
Understanding when to use each approach:
data-show - CSS Display Toggle
How it works: Sets display: none on the element when false. The element remains in the DOM.
Pros:
- Fast toggle (no DOM manipulation)
- Preserves form input state
- Good for frequently toggled content
Cons:
- Hidden elements still consume memory
- JavaScript in hidden elements still runs
<div data-show="isVisible">
Content here
</div>
data-render - DOM Insertion/Removal
How it works: Completely removes the element from the DOM when false. A placeholder comment marks the position.
Pros:
- Reduces DOM size when hidden
- Components only initialize when rendered
- Better for heavy/complex content
Cons:
- Slower toggle (DOM manipulation)
- Form state is lost when removed
<div data-render="shouldRender">
Heavy content here
</div>
data-show as your default choice. Switch to data-render when you have heavy content that's rarely shown, or when you need components to reinitialize each time they appear.
Complex Conditional Logic
Use computed properties for complex conditional rendering logic. This example demonstrates a comprehensive permissions system with role-based access control, account status checks, subscription levels, and feature flags:
<div data-component="complex-conditionals">
<p class="text-muted">Demonstrates advanced conditional rendering with role-based access control, account status checks, subscription levels, and feature flags.</p>
<!-- Configuration Controls -->
<div class="row mb-4">
<div class="col-md-3">
<label class="form-label fw-bold">User Role:</label>
<select data-model="userRole" class="form-select">
<option value="guest">Guest</option>
<option value="user">User</option>
<option value="admin">Admin</option>
<option value="superadmin">Super Admin</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold">Account Status:</label>
<select data-model="accountStatus" class="form-select">
<option value="active">Active</option>
<option value="suspended">Suspended</option>
<option value="pending">Pending</option>
<option value="inactive">Inactive</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold">Subscription:</label>
<select data-model="subscription" class="form-select">
<option value="free">Free</option>
<option value="pro">Pro</option>
<option value="enterprise">Enterprise</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold">Feature Flags:</label>
<div>
<input type="checkbox" data-model="features.beta" id="beta-flag" class="form-check-input">
<label for="beta-flag" class="form-check-label">Beta Features</label>
</div>
<div class="mt-1">
<input type="checkbox" data-model="features.experimental" id="experimental-flag" class="form-check-input">
<label for="experimental-flag" class="form-check-label">Experimental</label>
</div>
</div>
</div>
<!-- Quick action buttons -->
<div class="mb-4">
<button data-action="setGuestUser" class="btn btn-sm btn-secondary me-2">Guest Mode</button>
<button data-action="setRegularUser" class="btn btn-sm btn-primary me-2">Regular User</button>
<button data-action="setProUser" class="btn btn-sm btn-success me-2">Pro User</button>
<button data-action="setAdmin" class="btn btn-sm btn-warning me-2">Admin</button>
<button data-action="setSuperAdmin" class="btn btn-sm btn-danger">Super Admin</button>
</div>
<!-- Basic role-based access -->
<div data-show="canViewDashboard" class="alert alert-success">
<h5>๐ Dashboard Access</h5>
<p>Welcome to your dashboard! You have authenticated user access.</p>
<small class="text-muted">Logic: <code>canViewDashboard</code> (user role + active account)</small>
</div>
<!-- Admin-only content -->
<div data-show="isAdmin" class="alert alert-warning">
<h5>โ๏ธ Admin Panel</h5>
<p>Administrative tools and settings are available to you.</p>
<small class="text-muted">Logic: <code>isAdmin</code> (admin or superadmin role)</small>
<!-- Super admin-only content -->
<div data-show="isSuperAdmin" class="border border-danger p-2 mt-2 bg-light">
<h6>๐ง Super Admin Tools</h6>
<p>System-level configuration and advanced administrative functions.</p>
<small class="text-muted">Logic: <code>isSuperAdmin</code> (superadmin role only)</small>
</div>
</div>
<!-- Premium features -->
<div data-show="hasPremiumAccess" class="alert alert-info">
<h5>โจ Premium Features</h5>
<p>Advanced analytics, reporting, and premium integrations.</p>
<small class="text-muted">Logic: <code>hasPremiumAccess</code> (pro/enterprise subscription + active account + user role)</small>
</div>
<!-- Beta features -->
<div data-show="canAccessBeta" class="alert alert-secondary">
<h5>๐งช Beta Features</h5>
<p>Experimental features and upcoming functionality (use at your own risk).</p>
<small class="text-muted">Logic: <code>canAccessBeta</code> (beta flag + active account + (premium subscription OR admin))</small>
</div>
<!-- Experimental features -->
<div data-show="canAccessExperimental" class="alert alert-dark">
<h5>๐ Experimental Features</h5>
<p>Cutting-edge features for super admins and enterprise users only.</p>
<small class="text-muted">Logic: <code>canAccessExperimental</code> (experimental flag + (superadmin OR enterprise))</small>
</div>
<!-- Account status warnings -->
<div data-show="accountNeedsAttention" class="alert alert-danger">
<h5>โ ๏ธ Account Issue</h5>
<p data-bind="accountMessage"></p>
<small class="text-muted">Logic: <code>accountNeedsAttention</code> (suspended/pending/inactive status)</small>
</div>
<!-- Feature matrix display -->
<div class="mt-4">
<h5>Current Permissions Matrix:</h5>
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead class="table-dark">
<tr>
<th>Feature</th>
<th>Access</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr>
<td>Dashboard</td>
<td><span data-bind="canViewDashboard ? 'โ
Yes' : 'โ No'" class="badge bg-secondary"></span></td>
<td><small>User role + Active account</small></td>
</tr>
<tr>
<td>Admin Panel</td>
<td><span data-bind="isAdmin ? 'โ
Yes' : 'โ No'" class="badge bg-secondary"></span></td>
<td><small>Admin or Super Admin role</small></td>
</tr>
<tr>
<td>Premium Features</td>
<td><span data-bind="hasPremiumAccess ? 'โ
Yes' : 'โ No'" class="badge bg-secondary"></span></td>
<td><small>Pro/Enterprise subscription + Active account + User role</small></td>
</tr>
<tr>
<td>Beta Features</td>
<td><span data-bind="canAccessBeta ? 'โ
Yes' : 'โ No'" class="badge bg-secondary"></span></td>
<td><small>Beta flag + Active account + (Premium OR Admin)</small></td>
</tr>
<tr>
<td>Experimental Features</td>
<td><span data-bind="canAccessExperimental ? 'โ
Yes' : 'โ No'" class="badge bg-secondary"></span></td>
<td><small>Experimental flag + (Super Admin OR Enterprise)</small></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Current state summary -->
<div class="mt-3 p-3 bg-light rounded">
<h6>Current State Summary:</h6>
<div class="row">
<div class="col-md-6">
<small>
<strong>Role:</strong> <span data-bind="userRole" class="badge bg-primary"></span><br>
<strong>Account:</strong> <span data-bind="accountStatus" class="badge bg-info"></span><br>
<strong>Subscription:</strong> <span data-bind="subscription" class="badge bg-success"></span>
</small>
</div>
<div class="col-md-6">
<small>
<strong>Beta Flag:</strong> <span data-bind="features.beta ? 'Enabled' : 'Disabled'" class="badge bg-secondary"></span><br>
<strong>Experimental:</strong> <span data-bind="features.experimental ? 'Enabled' : 'Disabled'" class="badge bg-secondary"></span>
</small>
</div>
</div>
</div>
</div>
wildflower.component('complex-conditionals', {
state: {
userRole: 'guest',
accountStatus: 'active',
subscription: 'free',
features: {
beta: false,
experimental: false
}
},
computed: {
// Basic role checks
isGuest() {
return this.userRole === 'guest'
},
isUser() {
return ['user', 'admin', 'superadmin'].includes(this.userRole)
},
isAdmin() {
return ['admin', 'superadmin'].includes(this.userRole)
},
isSuperAdmin() {
return this.userRole === 'superadmin'
},
// Account status checks
isAccountActive() {
return this.accountStatus === 'active'
},
accountNeedsAttention() {
return ['suspended', 'pending', 'inactive'].includes(this.accountStatus)
},
// Subscription checks
hasPremiumSubscription() {
return ['pro', 'enterprise'].includes(this.subscription)
},
hasEnterpriseSubscription() {
return this.subscription === 'enterprise'
},
// Complex permission logic combining multiple factors
canViewDashboard() {
return this.isUser && this.isAccountActive
},
hasPremiumAccess() {
return this.hasPremiumSubscription &&
this.isAccountActive &&
this.isUser
},
canAccessBeta() {
return this.features.beta &&
this.isAccountActive &&
(this.hasPremiumSubscription || this.isAdmin)
},
canAccessExperimental() {
return this.features.experimental &&
(this.isSuperAdmin || this.hasEnterpriseSubscription)
},
// Dynamic messages based on account status
accountMessage() {
switch (this.accountStatus) {
case 'suspended':
return 'Your account has been suspended. Please contact support to resolve this issue.'
case 'pending':
return 'Your account is pending approval. Please wait for verification to complete.'
case 'inactive':
return 'Your account is inactive. Please reactivate your account to continue.'
default:
return 'Account status unclear. Please contact support.'
}
}
},
// Quick setup methods for demonstration
setGuestUser() {
this.userRole = 'guest'
this.accountStatus = 'active'
this.subscription = 'free'
this.features.beta = false
this.features.experimental = false
},
setRegularUser() {
this.userRole = 'user'
this.accountStatus = 'active'
this.subscription = 'free'
this.features.beta = false
this.features.experimental = false
},
setProUser() {
this.userRole = 'user'
this.accountStatus = 'active'
this.subscription = 'pro'
this.features.beta = true
this.features.experimental = false
},
setAdmin() {
this.userRole = 'admin'
this.accountStatus = 'active'
this.subscription = 'pro'
this.features.beta = true
this.features.experimental = false
},
setSuperAdmin() {
this.userRole = 'superadmin'
this.accountStatus = 'active'
this.subscription = 'enterprise'
this.features.beta = true
this.features.experimental = true
}
})
Conditional Lists and Iteration
Combine conditional rendering with list iteration for dynamic interfaces that filter and display data based on user preferences or data conditions. This powerful pattern enables sophisticated filtering, categorization, and interactive list management.
data-show within data-list templates to create conditional list items, combine filtering with status-based rendering, and manage complex list states with computed properties.
<div data-component="conditional-lists">
<p class="text-muted mb-4">Demonstrates filtering list items based on status, priority, and categories with conditional rendering for different item states.</p>
<!-- Advanced filtering controls -->
<div class="row mb-4">
<div class="col-md-4">
<label class="form-label fw-bold">Status Filters:</label>
<div class="form-check">
<input type="checkbox" data-model="filters.showCompleted" id="completed" class="form-check-input">
<label for="completed" class="form-check-label">โ
Completed (<span data-bind="completedCount"></span>)</label>
</div>
<div class="form-check">
<input type="checkbox" data-model="filters.showPending" id="pending" class="form-check-input">
<label for="pending" class="form-check-label">โณ Pending (<span data-bind="pendingCount"></span>)</label>
</div>
<div class="form-check">
<input type="checkbox" data-model="filters.showCancelled" id="cancelled" class="form-check-input">
<label for="cancelled" class="form-check-label">โ Cancelled (<span data-bind="cancelledCount"></span>)</label>
</div>
</div>
<div class="col-md-4">
<label class="form-label fw-bold">Priority Filters:</label>
<div class="form-check">
<input type="checkbox" data-model="filters.showHighPriority" id="high-priority" class="form-check-input">
<label for="high-priority" class="form-check-label">๐ฅ High Priority (<span data-bind="highPriorityCount"></span>)</label>
</div>
<div class="form-check">
<input type="checkbox" data-model="filters.showMediumPriority" id="medium-priority" class="form-check-input">
<label for="medium-priority" class="form-check-label">โก Medium Priority (<span data-bind="mediumPriorityCount"></span>)</label>
</div>
<div class="form-check">
<input type="checkbox" data-model="filters.showLowPriority" id="low-priority" class="form-check-input">
<label for="low-priority" class="form-check-label">๐ Low Priority (<span data-bind="lowPriorityCount"></span>)</label>
</div>
</div>
<div class="col-md-4">
<label class="form-label fw-bold">Category Filter:</label>
<select data-model="filters.selectedCategory" class="form-select">
<option value="all">All Categories</option>
<option value="development">Development</option>
<option value="design">Design</option>
<option value="marketing">Marketing</option>
<option value="operations">Operations</option>
</select>
<div class="mt-2">
<input type="checkbox" data-model="filters.hideOverdue" id="hide-overdue" class="form-check-input">
<label for="hide-overdue" class="form-check-label">Hide Overdue</label>
</div>
</div>
</div>
<!-- Action buttons and stats -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<button data-action="addRandomTask" class="btn btn-primary me-2">Add Random Task</button>
<button data-action="clearFilters" class="btn btn-secondary me-2">Clear Filters</button>
<button data-action="showAll" class="btn btn-info">Show All</button>
</div>
<div class="text-muted">
<small>
Showing <span data-bind="visibleItemsCount" class="fw-bold"></span> of
<span data-bind="items.length" class="fw-bold"></span> tasks
</small>
</div>
</div>
<!-- Conditional list rendering based on filters -->
<div data-show="hasVisibleItems">
<div data-list="filteredItems" class="task-list">
<template>
<div class="card mb-3 shadow-sm">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<h6 class="mb-0 me-2" data-bind="title"></h6>
<span class="badge me-2"
data-bind-class="priority === 'high' ? 'bg-danger' : priority === 'medium' ? 'bg-warning' : 'bg-secondary'"
data-bind="priority"></span>
<span class="badge bg-info" data-bind="category"></span>
</div>
<div>
<span class="badge me-2"
data-bind-class="status === 'completed' ? 'bg-success' : status === 'pending' ? 'bg-warning text-dark' : 'bg-danger'"
data-bind="status"></span>
<span data-show="isOverdue" class="badge bg-dark">OVERDUE</span>
</div>
</div>
<div class="card-body">
<p class="text-muted mb-2" data-bind="description"></p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">
Due: <span data-bind="dueDate"></span>
</small>
<div>
<button data-action="toggleStatus" class="btn btn-sm btn-primary me-1">
<span data-bind="status === 'completed' ? 'Reopen' : 'Complete'"></span>
</button>
<button data-action="changePriority" class="btn btn-sm btn-secondary me-1">Priority</button>
<button data-action="deleteTask" class="btn btn-sm btn-danger">Delete</button>
</div>
</div>
</div>
<!-- Status-specific conditional footers -->
<div data-show="status === 'completed'" class="card-footer bg-success text-white">
<div class="d-flex justify-content-between align-items-center">
<span>โ
Completed on <span data-bind="completedDate"></span></span>
<small>Completed <span data-bind="completedDaysAgo"></span> days ago</small>
</div>
</div>
<div data-show="status === 'pending'" class="card-footer bg-warning text-dark">
<div class="d-flex justify-content-between align-items-center">
<span>โณ In progress since <span data-bind="startedDate"></span></span>
<small>Started <span data-bind="startedDaysAgo"></span> days ago</small>
</div>
</div>
<div data-show="status === 'cancelled'" class="card-footer bg-danger text-white">
<div class="d-flex justify-content-between align-items-center">
<span>โ Cancelled: <span data-bind="cancellationReason"></span></span>
<small>Cancelled <span data-bind="cancelledDaysAgo"></span> days ago</small>
</div>
</div>
<!-- Priority-specific notifications -->
<div data-show="priority === 'high' && status === 'pending'" class="card-footer bg-light border-danger">
<small class="text-danger"><strong>๐ฅ High Priority:</strong> This task requires immediate attention!</small>
</div>
<div data-show="isOverdueAndPending" class="card-footer bg-dark text-white">
<small><strong>โ ๏ธ OVERDUE:</strong> This task is past its due date and needs urgent action.</small>
</div>
</div>
</template>
</div>
</div>
<!-- Advanced empty state handling -->
<div data-show="!hasVisibleItems" class="text-center py-5">
<div data-show="items.length === 0" class="alert alert-info">
<h5>๐ No Tasks Yet</h5>
<p>Get started by adding your first task using the "Add Random Task" button above.</p>
<button data-action="addRandomTask" class="btn btn-primary">Add Your First Task</button>
</div>
<div data-show="items.length > 0 && visibleItemsCount === 0" class="alert alert-warning">
<h5>๐ No Tasks Match Your Filters</h5>
<p>Try adjusting your filters to see more tasks, or add a new task that matches your criteria.</p>
<div class="mt-3">
<button data-action="clearFilters" class="btn btn-secondary me-2">Clear All Filters</button>
<button data-action="showAll" class="btn btn-info">Show All Tasks</button>
</div>
</div>
</div>
<!-- Advanced statistics panel -->
<div class="mt-4 p-3 bg-light rounded">
<h6>๐ Task Statistics:</h6>
<div class="row">
<div class="col-md-3">
<small>
<strong>Total:</strong> <span data-bind="items.length" class="badge bg-primary"></span><br>
<strong>Visible:</strong> <span data-bind="visibleItemsCount" class="badge bg-info"></span>
</small>
</div>
<div class="col-md-3">
<small>
<strong>Completed:</strong> <span data-bind="completedCount" class="badge bg-success"></span><br>
<strong>Pending:</strong> <span data-bind="pendingCount" class="badge bg-warning"></span>
</small>
</div>
<div class="col-md-3">
<small>
<strong>High Priority:</strong> <span data-bind="highPriorityCount" class="badge bg-danger"></span><br>
<strong>Overdue:</strong> <span data-bind="overdueCount" class="badge bg-dark"></span>
</small>
</div>
<div class="col-md-3">
<small>
<strong>Completion Rate:</strong> <span data-bind="completionRate" class="badge bg-secondary"></span>%<br>
<strong>Active Filters:</strong> <span data-bind="activeFiltersCount" class="badge bg-secondary"></span>
</small>
</div>
</div>
</div>
</div>
wildflower.component('conditional-lists', {
state: {
// Advanced filtering state
filters: {
showCompleted: true,
showPending: true,
showCancelled: false,
showHighPriority: true,
showMediumPriority: true,
showLowPriority: true,
selectedCategory: 'all',
hideOverdue: false
},
// Sample task data with comprehensive properties
items: [
{
id: 1,
title: 'Complete project proposal',
description: 'Finalize the Q1 project proposal with budget estimates and timeline',
status: 'completed',
priority: 'high',
category: 'development',
dueDate: '2024-01-15',
startedDate: '2024-01-10',
completedDate: '2024-01-14'
},
{
id: 2,
title: 'Review code changes',
description: 'Conduct thorough code review for the new authentication system',
status: 'pending',
priority: 'medium',
category: 'development',
dueDate: '2024-01-20',
startedDate: '2024-01-16'
},
{
id: 3,
title: 'Deploy to production',
description: 'Deploy the latest version to production environment',
status: 'cancelled',
priority: 'low',
category: 'operations',
dueDate: '2024-01-18',
cancellationReason: 'Requirements changed',
cancelledDate: '2024-01-17'
},
{
id: 4,
title: 'Design new landing page',
description: 'Create mockups and prototypes for the new marketing landing page',
status: 'pending',
priority: 'high',
category: 'design',
dueDate: '2024-01-12',
startedDate: '2024-01-08'
},
{
id: 5,
title: 'Plan marketing campaign',
description: 'Develop comprehensive marketing strategy for Q1 product launch',
status: 'completed',
priority: 'medium',
category: 'marketing',
dueDate: '2024-01-10',
startedDate: '2024-01-05',
completedDate: '2024-01-09'
}
],
nextId: 6
},
computed: {
// Status-based counts
completedCount() {
return this.items.filter(item => item.status === 'completed').length
},
pendingCount() {
return this.items.filter(item => item.status === 'pending').length
},
cancelledCount() {
return this.items.filter(item => item.status === 'cancelled').length
},
// Priority-based counts
highPriorityCount() {
return this.items.filter(item => item.priority === 'high').length
},
mediumPriorityCount() {
return this.items.filter(item => item.priority === 'medium').length
},
lowPriorityCount() {
return this.items.filter(item => item.priority === 'low').length
},
// Advanced computed properties
overdueCount() {
const today = new Date().toISOString().split('T')[0]
return this.items.filter(item =>
item.status === 'pending' && item.dueDate < today
).length
},
visibleItemsCount() {
return this.filteredItems.length
},
hasVisibleItems() {
return this.visibleItemsCount > 0
},
completionRate() {
if (this.items.length === 0) return 0
return Math.round((this.completedCount / this.items.length) * 100)
},
activeFiltersCount() {
let count = 0
const filters = this.filters
if (!filters.showCompleted) count++
if (!filters.showPending) count++
if (!filters.showCancelled) count++
if (!filters.showHighPriority) count++
if (!filters.showMediumPriority) count++
if (!filters.showLowPriority) count++
if (filters.selectedCategory !== 'all') count++
if (filters.hideOverdue) count++
return count
},
filteredItems() {
const filters = this.filters
return this.items.filter(item => {
const statusVisible =
(item.status === 'completed' && filters.showCompleted) ||
(item.status === 'pending' && filters.showPending) ||
(item.status === 'cancelled' && filters.showCancelled)
if (!statusVisible) return false
const priorityVisible =
(item.priority === 'high' && filters.showHighPriority) ||
(item.priority === 'medium' && filters.showMediumPriority) ||
(item.priority === 'low' && filters.showLowPriority)
if (!priorityVisible) return false
if (filters.selectedCategory !== 'all' && item.category !== filters.selectedCategory) return false
if (filters.hideOverdue && item.status === 'pending') {
const today = new Date().toISOString().split('T')[0]
if (item.dueDate < today) return false
}
return true
})
},
// Item-scoped computed properties for template use
isOverdue() {
const today = new Date().toISOString().split('T')[0]
return this.status === 'pending' && this.dueDate < today
},
isOverdueAndPending() {
const today = new Date().toISOString().split('T')[0]
return this.status === 'pending' && this.dueDate < today
},
completedDaysAgo() {
if (!this.completedDate) return 0
const completed = new Date(this.completedDate)
const today = new Date()
const diffTime = Math.abs(today - completed)
return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
},
startedDaysAgo() {
if (!this.startedDate) return 0
const started = new Date(this.startedDate)
const today = new Date()
const diffTime = Math.abs(today - started)
return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
},
cancelledDaysAgo() {
if (!this.cancelledDate) return 0
const cancelled = new Date(this.cancelledDate)
const today = new Date()
const diffTime = Math.abs(today - cancelled)
return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
}
},
// Task management actions
addRandomTask() {
const taskTemplates = [
{ title: 'Review documentation', description: 'Update and review project documentation', category: 'development' },
{ title: 'Conduct user testing', description: 'Gather feedback from beta users', category: 'design' },
{ title: 'Optimize database queries', description: 'Improve application performance', category: 'development' },
{ title: 'Create social media content', description: 'Develop engaging posts for social platforms', category: 'marketing' },
{ title: 'Server maintenance', description: 'Perform routine server maintenance tasks', category: 'operations' },
{ title: 'Design system updates', description: 'Update component library and style guide', category: 'design' },
{ title: 'Analytics report', description: 'Compile monthly analytics and metrics', category: 'marketing' }
]
const statuses = ['completed', 'pending', 'cancelled']
const priorities = ['high', 'medium', 'low']
const template = taskTemplates[Math.floor(Math.random() * taskTemplates.length)]
const status = statuses[Math.floor(Math.random() * statuses.length)]
const priority = priorities[Math.floor(Math.random() * priorities.length)]
// Generate random dates
const startDate = new Date()
startDate.setDate(startDate.getDate() - Math.floor(Math.random() * 30))
const dueDate = new Date(startDate)
dueDate.setDate(dueDate.getDate() + Math.floor(Math.random() * 14) + 1)
const newTask = {
id: this.nextId++,
title: template.title,
description: template.description,
status: status,
priority: priority,
category: template.category,
dueDate: dueDate.toISOString().split('T')[0],
startedDate: startDate.toISOString().split('T')[0]
}
// Add status-specific data
if (status === 'completed') {
const completedDate = new Date(startDate)
completedDate.setDate(completedDate.getDate() + Math.floor(Math.random() * 7))
newTask.completedDate = completedDate.toISOString().split('T')[0]
} else if (status === 'cancelled') {
const reasons = ['Requirements changed', 'Budget constraints', 'Timeline conflict', 'Resource unavailable']
newTask.cancellationReason = reasons[Math.floor(Math.random() * reasons.length)]
newTask.cancelledDate = new Date().toISOString().split('T')[0]
}
this.items.push(newTask)
},
clearFilters() {
this.filters = {
showCompleted: false,
showPending: false,
showCancelled: false,
showHighPriority: false,
showMediumPriority: false,
showLowPriority: false,
selectedCategory: 'all',
hideOverdue: false
}
},
showAll() {
this.filters = {
showCompleted: true,
showPending: true,
showCancelled: true,
showHighPriority: true,
showMediumPriority: true,
showLowPriority: true,
selectedCategory: 'all',
hideOverdue: false
}
},
// Item-level actions (called from within list templates)
toggleStatus(event, element, details) {
const index = details.index
const task = this.items[index]
if (task.status === 'completed') {
task.status = 'pending'
delete task.completedDate
} else if (task.status === 'pending') {
task.status = 'completed'
task.completedDate = new Date().toISOString().split('T')[0]
} else if (task.status === 'cancelled') {
task.status = 'pending'
delete task.cancellationReason
delete task.cancelledDate
}
},
changePriority(event, element, details) {
const index = details.index
const task = this.items[index]
const priorities = ['low', 'medium', 'high']
const currentIndex = priorities.indexOf(task.priority)
const nextIndex = (currentIndex + 1) % priorities.length
task.priority = priorities[nextIndex]
},
deleteTask(event, element, details) {
const index = details.index
this.items.splice(index, 1)
}
})
Performance Considerations
- Prefer
data-showfor frequently toggled content (fast CSS toggle) - Use
data-renderfor heavy content that's rarely shown (saves DOM size) - Use computed properties for complex multi-condition logic
- Group related conditional logic in computed properties
Conditional Rendering Patterns
| Pattern | Use Case | Example |
|---|---|---|
| Simple Toggle | Basic show/hide functionality | data-show="isVisible" |
| Role-based Access | User permission checks | data-show="isAdmin" |
| Feature Flags | Gradual feature rollout | data-show="features.newUI" |
| Data-driven | Content based on API data | data-show="user.subscription === 'premium'" |
| Complex Logic | Multiple conditions | data-show="canAccessFeature" |
Best Practices
โ Do
- Use computed properties for complex conditions
- Choose
data-showvsdata-renderbased on toggle frequency - Provide fallback content for empty states
- Group related conditional logic in computed properties
- Use semantic HTML for accessibility
โ Don't
- Create overly complex inline conditions
- Nest conditional logic too deeply
- Forget to handle edge cases and empty states
- Duplicate conditional logic across templates
- Mix conditional and display logic unnecessarily