Advanced Lists
Advanced list patterns including filtering, sorting, external data sources, and performance optimization.
Filtered and Sorted Lists
Use computed properties to create filtered and sorted views of your data:
<div data-component="product-catalog">
<div class="mb-3">
<input type="text" data-model="search"
class="form-control" placeholder="Search products...">
</div>
<div class="d-flex flex-wrap gap-2 mb-3">
<select data-model="category" class="form-select" style="width: auto;">
<option value="">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
<option value="books">Books</option>
</select>
<select data-model="sortBy" class="form-select" style="width: auto;">
<option value="name">Sort by Name</option>
<option value="price">Sort by Price</option>
</select>
<span class="badge bg-primary align-self-center">
<span data-bind="filtered.length"></span> of
<span data-bind="products.length"></span>
</span>
</div>
<ul class="list-group" data-list="filtered">
<template>
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong data-bind="name"></strong>
<span class="badge bg-secondary ms-2" data-bind="category"></span>
</div>
<div class="d-flex align-items-center gap-2">
<span data-bind="'$' + price"></span>
<button data-action="remove" class="btn btn-sm btn-danger">×</button>
</div>
</li>
</template>
</ul>
<div data-show="filtered.length === 0" class="text-muted text-center p-3">
No products match your filters.
</div>
</div>
wildflower.component('product-catalog', {
state: {
search: '',
category: '',
sortBy: 'name',
products: [
{ name: 'Headphones', category: 'electronics', price: 79 },
{ name: 'T-Shirt', category: 'clothing', price: 25 },
{ name: 'JavaScript Guide', category: 'books', price: 35 },
{ name: 'USB Cable', category: 'electronics', price: 12 },
{ name: 'Running Shoes', category: 'clothing', price: 89 },
{ name: 'Design Patterns', category: 'books', price: 42 }
]
},
computed: {
filtered() {
let items = [...this.products]
if (this.search) {
const term = this.search.toLowerCase()
items = items.filter(p =>
p.name.toLowerCase().includes(term))
}
if (this.category) {
items = items.filter(p =>
p.category === this.category)
}
items.sort((a, b) => this.sortBy === 'price'
? a.price - b.price
: a.name.localeCompare(b.name))
return items
}
},
remove(e, el, detail) {
const idx = this.products.indexOf(detail.item)
if (idx !== -1) this.products.splice(idx, 1)
}
})
Lists from External Data Sources
For simple store-backed lists, use the $ accessor directly in HTML:
<!-- Simplest: bind to store array directly -->
<div data-list="$notification-store.items" data-key="id">
<template>
<div class="notification">
<strong data-bind="title"></strong>
<p data-bind="message"></p>
</div>
</template>
</div>
For filtering or transformation, use subscribe with this.stores in computed properties:
Store-Based Lists
Access store arrays reactively using computed properties when you need to transform the data:
<!-- HTML: Render a list from a store -->
<div data-component="notification-list">
<h3>Notifications (<span data-bind="unreadCount">0</span> unread)</h3>
<div data-list="notifications">
<template>
<div class="notification">
<strong data-bind="title"></strong>
<p data-bind="message"></p>
<small data-bind="timestamp"></small>
</div>
</template>
</div>
</div>
// JavaScript: Component with subscribe accessing store data
wildflower.component('notification-list', {
subscribe: {
'notification-store': ['items']
},
computed: {
// Access store array via this.stores - automatically reactive
notifications() {
return this.stores['notification-store'].items || []
},
// Derived computed from store data
unreadCount() {
return this.notifications.filter(n => !n.read).length
}
},
markAsRead(event) {
const store = wildflower.getStore('notification-store')
const id = event.target.dataset.notificationId
store.markAsRead(id)
}
})
Cross-Component Lists
Render lists that depend on data from sibling components:
// A shopping cart component displaying items from a product store
wildflower.component('cart-summary', {
subscribe: {
'cart-store': ['selectedProducts']
},
computed: {
// Get selected items from store
cartItems() {
return this.stores['cart-store'].selectedProducts || []
},
// Apply transformations to store data
cartItemsWithTotals() {
return this.cartItems.map(item => ({
...item,
total: item.price * item.quantity
}))
},
grandTotal() {
return this.cartItemsWithTotals.reduce(
(sum, item) => sum + item.total, 0
)
}
}
})
Combined Local and Store Data
Filter or sort store data using local component state:
wildflower.component('filtered-user-list', {
state: {
searchTerm: '',
showOnlineOnly: false,
sortBy: 'name'
},
subscribe: {
'user-store': ['users']
},
computed: {
// Get raw data from store
allUsers() {
return this.stores['user-store'].users || []
},
// Apply local filters to store data
filteredUsers() {
let users = this.allUsers
// Filter by search term
if (this.searchTerm) {
const term = this.searchTerm.toLowerCase()
users = users.filter(u =>
u.name.toLowerCase().includes(term) ||
u.email.toLowerCase().includes(term)
)
}
// Filter by online status
if (this.showOnlineOnly) {
users = users.filter(u => u.isOnline)
}
// Sort by selected field
users.sort((a, b) => {
if (a[this.sortBy] < b[this.sortBy]) return -1
if (a[this.sortBy] > b[this.sortBy]) return 1
return 0
})
return users
}
}
})
- Automatic Reactivity: When the source data changes (store or component), the list automatically updates
- Computed Chaining: Chain computed properties to build complex filtered/sorted views
- Local State Control: Use local state for filters/sorting while keeping source data in stores
- Always Return Arrays: Ensure your computed properties return arrays (use
|| []fallback)