Skip to content

Every kind of field ​

Task: a form that uses each kind of field, with labels.

yaml
# Recipe: every kind of field, laid out with ui:form and ui:formitem.
paths:
  components: ./components

ui:formitem label="…" puts a label next to its field. ui:select takes its choices from ui:options with their own texts, and ui:radio takes them from the enum of the action's q:param. A ui:switch is a boolean box, and rows makes a multi-line field. The rules of each field come from the action (Show each error on its field).

xml
<q:component name="Booking">
  <q:action name="book" method="POST">
    <q:param name="name" required="true" minlength="2" />
    <q:param name="nights" type="integer" required="true" min="1" max="14" />
    <q:param name="room" enum="single,double,suite" default="double" />
    <q:param name="breakfast" type="boolean" default="false" />
    <q:param name="arrival" enum="morning,afternoon,night" default="afternoon" />
    <q:param name="notes" maxlength="300" default="" />
    <q:redirect url="/" flash="{name}: {nights} nights, {room}, breakfast {'yes' if breakfast else 'no'}, {arrival}." />
  </q:action>

  <ui:window title="Book a room">
    <ui:vbox gap="md" padding="lg" width="480">
      <q:if condition="flash">
        <ui:alert variant="{flashType == 'error' and 'danger' or 'success'}">{flash}</ui:alert>
      </q:if>
      <!-- ui:formitem puts a label next to its field. The fields take their
           rules (and the lists of choices) from the action's q:params. -->
      <ui:form on-submit="book" submit="Book">
        <ui:formitem label="Name"><ui:input bind="name" /></ui:formitem>
        <ui:formitem label="Nights"><ui:input bind="nights" /></ui:formitem>
        <ui:formitem label="Room">
          <ui:select bind="room">
            <ui:option value="single">Single</ui:option>
            <ui:option value="double">Double</ui:option>
            <ui:option value="suite">Suite</ui:option>
          </ui:select>
        </ui:formitem>
        <ui:switch bind="breakfast" label="Breakfast" />
        <ui:formitem label="Arrival"><ui:radio bind="arrival" /></ui:formitem>
        <ui:formitem label="Notes"><ui:input bind="notes" rows="3" /></ui:formitem>
      </ui:form>
    </ui:vbox>
  </ui:window>
</q:component>
xml
<q:test name="the form shows its labels, the option texts and the button" page="/">
  <test:visit />
  <test:expect text="Name" />
  <test:expect text="Single Double Suite" />
  <test:expect text="Breakfast" />
  <test:expect text="morning" />
  <test:expect text="Book" />
</q:test>

<q:test name="the fields reach the action typed" page="/">
  <test:submit action="book" name="Ana" nights="3" room="suite" breakfast="on" arrival="night" />
  <test:expect redirect="/" flash="Ana: 3 nights, suite, breakfast yes, night." />
</q:test>

<q:test name="left out, the defaults apply" page="/">
  <test:submit action="book" name="Ana" nights="1" />
  <test:expect flash="Ana: 1 nights, double, breakfast no, afternoon." />
</q:test>

<q:test name="a refused field keeps the others as typed" page="/">
  <test:submit action="book" name="Ana" nights="20" notes="Late check-in, please." />
  <test:expect error="nights" message="Must be at most 14 (got 20)" />
  <test:expect text="Late check-in, please." />
</q:test>
text
tests/booking.test.q
  PASS  the form shows its labels, the option texts and the button
  PASS  the fields reach the action typed
  PASS  left out, the defaults apply
  PASS  a refused field keeps the others as typed
4 passed, 0 failed

See UI-9 and UI-14.

MIT Licensed · Built with VitePress