Live filter over an array in state. No data-list.
Built with wildflower.nano.min.js
<div data-component="searchable-list">
<input type="search" data-action="input:filter" placeholder="Search...">
<span data-bind="count"></span> of <span data-bind="total"></span> shown
<span data-show="hasQuery">filtering "<span data-bind="query"></span>"</span>
<div id="results"></div>
</div>
<!-- One row, written once. Same shape a data-list template would hold. -->
<template id="row-template">
<div class="row">
<span class="code"></span>
<span class="text"></span>
<span class="badge"></span>
</div>
</template>
// Plain data. It never changes, so it does not need to be reactive state.
const CODES = [
[200,'OK'], [404,'Not Found'], [500,'Internal Server Error'] /* ...etc */
].map(([code, text]) => ({ code, text, group: Math.floor(code / 100) }));
const tpl = document.getElementById('row-template');
wildflower.component('searchable-list', {
state: { query: '' },
computed: {
// A computed derived from the query. Cheap and declarative — keep it reactive.
results() {
const q = this.query.trim().toLowerCase();
if (!q) return CODES;
return CODES.filter(c => String(c.code).includes(q) || c.text.toLowerCase().includes(q));
},
count() { return this.results.length; },
total() { return CODES.length; },
hasQuery() { return this.query.trim().length > 0; }
},
init() { this.renderRows(); },
// The action both updates state (so the reactive count/label repaint)
// and re-renders the list body.
filter(event) {
this.query = event.target.value;
this.renderRows();
},
// The one imperative bit: replace the visible set. For a filtered view you
// are re-rendering all of it anyway, so keyed diffing buys nothing.
renderRows() {
const list = this.element.querySelector('#results');
list.textContent = '';
for (const c of this.results) {
const el = tpl.content.firstElementChild.cloneNode(true);
el.querySelector('.code').textContent = c.code;
el.querySelector('.text').textContent = c.text;
const badge = el.querySelector('.badge');
badge.textContent = c.group + 'xx';
badge.classList.add('g' + c.group);
list.appendChild(el);
}
}
});