Debounced Search with data-query

Debounce the keystrokes, re-run the query, and let it discard the answers that arrive too late.

The plain Debounced Search pattern debounces the input and calls a search function, and it is on you to ignore a slow response to an earlier term that lands after a faster one. With a data-query (Full build) the term lives in a store the source reads, each debounced keystroke calls refresh(), and the query supersedes the request still in flight, so the list only ever shows the answer to what was typed last.

Live Demo

The demo runs in its own frame on the Full build; its pretend server answers in a random 0.3 to 1.3 seconds so the out-of-order case actually happens. Open it on its own ↗

Source

HTML + JavaScript
<div data-component="plant-search">
    <input type="search" data-model="q"
           data-action="input:onSearch" data-event-debounce="400">
    <span data-show="$results.isStale">Searching…</span>
    <div data-list="results" data-key="id">
        <template><div data-bind="name"></div></template>
    </div>
    <p data-show="!$results.isStale && $results.count === 0 && q">No matches.</p>
</div>

<script>
wildflower.store('search', { state: { q: '' } });

wildflower.query('results', {
    from: '/api/plants',
    key: 'id',
    params: () => ({ q: wildflower.getStore('search').q })
});

wildflower.component('plant-search', {
    state: { q: '' },
    computed: {
        results() { return wildflower.getQuery('results').rows; }
    },
    onSearch() {
        wildflower.getStore('search').q = this.q;
        wildflower.getQuery('results').refresh();   // supersedes any request still in flight
    }
});
</script>

Key Points

  • data-event-debounce="400" is the same attribute the plain pattern uses; the difference is what the handler does: set the term and call refresh()
  • Last call wins: a newer refresh() supersedes the request still in flight, and a slow answer to an earlier term is discarded instead of overwriting the list, with no request counter of your own
  • The term lives in a store that the source reads through params, so every request the query makes, including a later focus refresh, searches for what is on screen
  • isStale is the spinner after the first load and count drives the empty state; the previous results stay on screen until the new ones arrive, so the list never flashes empty between keystrokes
  • Needs the Full build; the plain pattern shows the same search against a hand-written call. Sources and params are in Sources