跳到正文

一个页面上的多个表单 ​

机器翻译

本页由英文原文机器翻译而来,尚未经过母语审校,欢迎在 GitHub 上提出修改。内容如有出入,以英文原文为准。

任务: 在一个页面上添加、删除和清空,每一项都有自己的动作。

yaml
# Recipe: several forms on one page, each posting to its own action.
paths:
  components: ./components
  migrations: ./migrations

datasources:
  db:
    driver: sqlite
    database: ./data/cart.db
sql
CREATE TABLE cart (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item TEXT NOT NULL,
    quantity INTEGER NOT NULL
);

INSERT INTO cart (item, quantity) VALUES ('Coffee', 1), ('Bread', 2);

每个 ui:form on-submit 和每个 ui:button on-click 都会在名为 action 的字段中 发送它的动作名;页面只运行这个动作,不运行其他动作。with="id={cart.id}" 随按钮发送这一行的 id。

xml
<q:component name="Cart">
  <!-- Three actions on one page. Each form (and each button) sends the name
       of its action in the field "action"; the page runs that one only. -->
  <q:action name="add" method="POST">
    <q:param name="item" required="true" maxlength="60" />
    <q:param name="quantity" type="integer" min="1" max="99" default="1" />
    <q:query name="added" datasource="db">
      INSERT INTO cart (item, quantity) VALUES (:item, :quantity)
      <q:param name="item" value="{item}" type="string" />
      <q:param name="quantity" value="{quantity}" type="integer" />
    </q:query>
    <q:redirect url="/" flash="Added {quantity} × {item}." />
  </q:action>

  <q:action name="remove" method="POST">
    <q:param name="id" type="integer" required="true" />
    <q:query name="removed" datasource="db">
      DELETE FROM cart WHERE id = :id
      <q:param name="id" value="{id}" type="integer" />
    </q:query>
    <q:redirect url="/" flash="Removed." />
  </q:action>

  <q:action name="empty" method="POST">
    <q:query name="emptied" datasource="db">DELETE FROM cart</q:query>
    <q:redirect url="/" flash="The cart is empty." />
  </q:action>

  <q:query name="cart" datasource="db">
    SELECT id, item, quantity FROM cart ORDER BY id
  </q:query>

  <ui:window title="Cart">
    <q:if condition="flash">
      <ui:alert variant="{flashType == 'error' and 'danger' or 'success'}">{flash}</ui:alert>
    </q:if>
    <ui:form on-submit="add">
      <ui:hbox gap="sm">
        <ui:input bind="item" placeholder="Item" grow="true" />
        <ui:input bind="quantity" width="80" />
        <ui:button variant="primary">Add</ui:button>
      </ui:hbox>
    </ui:form>
    <q:loop query="cart">
      <ui:hbox gap="sm" align="center">
        <ui:text>{cart.quantity} × {cart.item}</ui:text>
        <!-- with= sends the row's id along with the action's name. -->
        <ui:button on-click="remove" with="id={cart.id}">Remove</ui:button>
      </ui:hbox>
    </q:loop>
    <ui:button on-click="empty" variant="danger">Empty the cart</ui:button>
  </ui:window>
</q:component>

在有多个动作的页面上,如果提交中缺少 action,或者它不对应任何一个动作, 就会返回 400 并列出页面上的动作。不会有其他动作代替它运行,最后一个测试检查了这一点:

xml
<q:test name="each form runs its own action" page="/">
  <test:submit action="add" item="Milk" quantity="3" />
  <test:expect flash="Added 3 × Milk." />
  <test:submit action="remove" id="1" />
  <test:expect flash="Removed." />
  <test:expect table="cart" count="2" />
  <test:expect table="cart" count="0" where="item = 'Coffee'" />
</q:test>

<q:test name="the button with no fields empties the cart" page="/">
  <test:submit action="empty" />
  <test:expect redirect="/" flash="The cart is empty." />
  <test:expect table="cart" count="0" />
</q:test>

<q:test name="a post that names no action of the page is refused" page="/">
  <test:submit action="checkout" />
  <test:expect status="400" />
  <test:expect text="No q:action named 'checkout' on this page. Available: add, remove, empty." />
  <test:expect table="cart" count="2" />
</q:test>
text
tests/cart.test.q
  PASS  each form runs its own action
  PASS  the button with no fields empties the cart
  PASS  a post that names no action of the page is refused
3 passed, 0 failed

参见 ACT-5。

MIT 许可证 · 使用 VitePress 构建