Dynamic List Example
This example demonstrates how to create a dynamic list with advanced features like sorting, filtering, and inline editing using VaneJS.
Complete Example
html
<!DOCTYPE html>
<html>
<head>
<title>VaneJS Dynamic List</title>
<script src="path/to/engineV2.js"></script>
<style>
.container {
max-width: 800px;
margin: 20px auto;
font-family: Arial, sans-serif;
}
.controls {
margin-bottom: 20px;
display: flex;
gap: 10px;
align-items: center;
}
.search {
flex: 1;
padding: 8px;
font-size: 16px;
}
.sort-button {
padding: 8px 16px;
background: #2196F3;
color: white;
border: none;
cursor: pointer;
}
.add-button {
padding: 8px 16px;
background: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background: #f5f5f5;
cursor: pointer;
}
th:hover {
background: #e0e0e0;
}
.editable {
cursor: pointer;
}
.editable:hover {
background: #f0f0f0;
}
.edit-input {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.actions {
display: flex;
gap: 5px;
}
.delete-btn {
padding: 5px 10px;
background: #ff4444;
color: white;
border: none;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>Dynamic List</h1>
<!-- Controls -->
<div class="controls">
<input
type="text"
class="search"
placeholder="Search..."
oninput="handleSearch(event)"
>
<button class="add-button" onclick="addItem()">Add Item</button>
</div>
<!-- Table -->
<table>
<thead>
<tr>
<th onclick="sortBy('id')">#</th>
<th onclick="sortBy('name')">Name</th>
<th onclick="sortBy('category')">Category</th>
<th onclick="sortBy('price')">Price</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-vn-repeat="filteredItems as item">
<tr>
<td data-vn-ritem="{item}.id"></td>
<td
class="editable"
onclick="startEdit(event, 'name')"
data-vn-ritem="{item}.id"
>
<span data-vn-if="!{editingItem} || {editingItem}.id !== {item}.id">
<span data-vn-ritem="{item}.name"></span>
</span>
<span data-vn-if="{editingItem} && {editingItem}.id === {item}.id">
<input
type="text"
class="edit-input"
onblur="finishEdit(event)"
onkeypress="handleEditKeyPress(event)"
data-vn-value="{item}.name"
>
</span>
</td>
<td
class="editable"
onclick="startEdit(event, 'category')"
data-vn-ritem="{item}.id"
>
<span data-vn-if="!{editingItem} || {editingItem}.id !== {item}.id">
<span data-vn-ritem="{item}.category"></span>
</span>
<span data-vn-if="{editingItem} && {editingItem}.id === {item}.id">
<input
type="text"
class="edit-input"
onblur="finishEdit(event)"
onkeypress="handleEditKeyPress(event)"
data-vn-value="{item}.category"
>
</span>
</td>
<td
class="editable"
onclick="startEdit(event, 'price')"
data-vn-ritem="{item}.id"
>
<span data-vn-if="!{editingItem} || {editingItem}.id !== {item}.id">
$<span data-vn-ritem="{item}.price"></span>
</span>
<span data-vn-if="{editingItem} && {editingItem}.id === {item}.id">
<input
type="number"
class="edit-input"
onblur="finishEdit(event)"
onkeypress="handleEditKeyPress(event)"
data-vn-value="{item}.price"
>
</span>
</td>
<td>
<div class="actions">
<button
class="delete-btn"
onclick="deleteItem(event)"
data-vn-ritem="{item}.id"
>Delete</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<script>
window.onload = function() {
// Initialize state
$setState("items", [
{ id: 1, name: "Laptop", category: "Electronics", price: 999.99 },
{ id: 2, name: "Desk Chair", category: "Furniture", price: 199.99 },
{ id: 3, name: "Coffee Maker", category: "Appliances", price: 49.99 }
]);
$setState("sortField", "id");
$setState("sortDirection", "asc");
$setState("searchQuery", "");
$setState("editingItem", null);
updateFilteredItems();
}
function updateFilteredItems() {
const items = $getState("items");
const query = $getState("searchQuery").toLowerCase();
const sortField = $getState("sortField");
const sortDirection = $getState("sortDirection");
let filtered = items;
// Apply search filter
if (query) {
filtered = items.filter(item =>
item.name.toLowerCase().includes(query) ||
item.category.toLowerCase().includes(query)
);
}
// Apply sorting
filtered.sort((a, b) => {
let comparison = 0;
if (a[sortField] < b[sortField]) comparison = -1;
if (a[sortField] > b[sortField]) comparison = 1;
return sortDirection === "asc" ? comparison : -comparison;
});
$setState("filteredItems", filtered);
}
function handleSearch(event) {
$setState("searchQuery", event.target.value);
updateFilteredItems();
}
function sortBy(field) {
const currentField = $getState("sortField");
const currentDirection = $getState("sortDirection");
const newDirection =
field === currentField && currentDirection === "asc"
? "desc"
: "asc";
$setState("sortField", field);
$setState("sortDirection", newDirection);
updateFilteredItems();
}
function addItem() {
const items = $getState("items");
const newItem = {
id: Date.now(),
name: "New Item",
category: "Uncategorized",
price: 0
};
$setState("items", [...items, newItem]);
updateFilteredItems();
}
function deleteItem(event) {
const itemId = parseInt(event.target.dataset.vnRitem);
const items = $getState("items");
$setState("items", items.filter(item => item.id !== itemId));
updateFilteredItems();
}
function startEdit(event, field) {
const itemId = parseInt(event.currentTarget.dataset.vnRitem);
const items = $getState("items");
const item = items.find(item => item.id === itemId);
if (item) {
$setState("editingItem", { ...item, field });
// Focus the input after it's rendered
setTimeout(() => {
const input = event.currentTarget.querySelector("input");
if (input) input.focus();
}, 0);
}
}
function finishEdit(event) {
const editingItem = $getState("editingItem");
if (!editingItem) return;
const items = $getState("items");
const value = event.target.value;
$setState("items", items.map(item =>
item.id === editingItem.id
? { ...item, [editingItem.field]: value }
: item
));
$setState("editingItem", null);
updateFilteredItems();
}
function handleEditKeyPress(event) {
if (event.key === "Enter") {
event.target.blur();
}
}
</script>
</body>
</html>
Key Features
Dynamic Data Management
- Add new items
- Delete existing items
- Inline editing
- Real-time updates
Sorting
- Sort by any column
- Toggle ascending/descending
- Visual feedback on sort column
Filtering
- Real-time search
- Filter by name or category
- Case-insensitive search
Inline Editing
- Click to edit any field
- Enter to save
- Blur to cancel
- Field-specific input types
Responsive Design
- Clean table layout
- Mobile-friendly controls
- Visual feedback on interactions
State Management
The app uses several state variables:
javascript
// Main data array
$setState("items", [
{ id: 1, name: "Item", category: "Category", price: 0 }
]);
// Sorting state
$setState("sortField", "id");
$setState("sortDirection", "asc");
// Search state
$setState("searchQuery", "");
// Editing state
$setState("editingItem", null);
// Filtered and sorted items
$setState("filteredItems", []);
Best Practices Demonstrated
Performance
- Efficient list updates
- Optimized sorting and filtering
- Minimal DOM updates
User Experience
- Immediate feedback
- Smooth transitions
- Clear visual cues
- Keyboard support
Code Organization
- Modular functions
- Clear state management
- Consistent naming
- Reusable components
Error Prevention
- Input validation
- Safe state updates
- Proper event handling
- Type-specific inputs