Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / UI

debounce

debounce: 300 — wait for a pause before running this element's event handler

`debounce: 300` tells a control to wait for a pause before it runs its event handler. Without it, an `onInput` handler fires on every keystroke — so a handler that asks the server a question would make one round-trip per character. With it, you get one, when the user stops typing.

stable1 example compiled by CIuiauthoringevents

Summary#

debounce: is a property of the control, written on the call. It coalesces that control's event handlers: the handler runs once, after the events stop for the given number of milliseconds.

Input(value: draft.Slug, onInput: CheckSlug, debounce: 300);

Type acme quickly and CheckSlug runs once, 300ms after the last keystroke — not four times.

Signature#

debounce: <milliseconds>      // a non-negative whole number; applies to this element's event handlers
  • It needs an event handler to coalesce (onInput, onClick, …). A debounce: with no handler is a compile error — it would do nothing, silently.
  • The handler runs with the latest value: the field's binding is written before the handler runs, so a handler that reads the field it is bound to sees what the user just typed.

Description#

Some handlers are cheap and want to fire on every event. Some ask a question that costs a round-trip — is this name available?, what matches this search? — and firing those per keystroke is wasteful and racy. debounce: is how you say "wait until they stop."

What debounce does not do. It does not deduplicate answers. If a handler asks the server something, a slow reply for an earlier value can still land after a faster reply for a later one. When the answer is written into state, guard it: re-read the field after the call and drop the answer if it no longer describes what's in the box (the example below does this). Debounce reduces the number of questions; it does not order the answers.

It is not validation. A friendly as-you-type check is a courtesy. The rules that actually protect your data are the ones declared on the entity — [Unique], [Required], [Pattern] — and those are enforced when the data is saved, whatever the client did or didn't check first.

Examples#

The is-this-name-taken check. An ordinary server function answers the question; an ordinary action asks it and writes the answer into state; debounce: makes it one round-trip per pause in typing:

entity Organization {
  [Required, MaxLength(100), Unique, Pattern("^[a-z0-9]+(-[a-z0-9]+)*$")] string Slug;
  string Name;
}

// An ordinary server function — it just answers a question.
bool CheckSlugAvailability(string candidate) {
  if (candidate == "") { return true; }
  return !Organization.Any(o => o.Slug == candidate);
}

[Page("/org/new")]
[Render(CSR)]
component OrgCreatePage() {
  Organization draft;
  bool slugFree = true;
  on mount { draft = new Organization {}; }

  // An action may write state and may call a server function. Re-reading the field after the call drops a stale
  // answer — the reply for a slug the user has already typed past.
  action CheckSlug() {
    var candidate = draft.Slug;
    var free = CheckSlugAvailability(candidate);
    if (candidate == draft.Slug) { slugFree = free; }
  }

  render {
    Stack(gap: 4) {
      Input(value: draft.Name, placeholder: "Organization name");
      Input(value: draft.Slug, placeholder: "team-slug", onInput: CheckSlug, debounce: 300);
      if (!slugFree) { Text("That slug is taken."); }
    }
  }
}

A search box wants the same thing — one query per pause, not one per letter:

Input(value: term, onInput: Search, debounce: 250);

See also#

Related

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

control — foreign UI controls (charts, grids, maps)

A `control` block declares the contract of a foreign UI widget — a chart, a data grid, a map — that a small JavaScript…

creating & saving data

A UI `action` creates, updates and deletes data by writing `new Entity { … }`, assigning fields, and calling…