Edit a row
Task: a page that edits one book, reached from the list.
yaml
# Recipe: open a form with a row's values and save it back.
paths:
components: ./components
migrations: ./migrations
datasources:
db:
driver: sqlite
database: ./data/books.dbsql
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(120) NOT NULL,
genre TEXT NOT NULL CHECK (genre IN ('novel', 'poetry', 'essay'))
);
INSERT INTO books (title, genre) VALUES ('Dom Casmurro', 'novel'), ('Libertinagem', 'poetry');The list links each book to /book/{id}:
xml
<q:component name="Books">
<q:query name="books" datasource="db">
SELECT id, title, genre FROM books ORDER BY id
</q:query>
<ui:window title="Books">
<q:if condition="flash">
<ui:alert variant="success">{flash}</ui:alert>
</q:if>
<q:loop query="books">
<ui:hbox gap="sm">
<ui:text>{books.title} ({books.genre})</ui:text>
<ui:link to="/book/{books.id}">Edit</ui:link>
</ui:hbox>
</q:loop>
</ui:window>
</q:component>components/book/[id].q answers /book/2 with id = 2. The action takes title and genre from the table (A form from the table) and values="{book}" opens the form with the row's values. A one-row query is enough.
xml
<q:component name="EditBook">
<!-- /book/2 gives id = 2. The action takes title and genre, with their
rules, from the table (UI-10); id comes from the URL. -->
<q:action name="save" method="POST" table="books" datasource="db" columns="title,genre">
<q:query name="saved" datasource="db">
UPDATE books SET title = :title, genre = :genre WHERE id = :id
<q:param name="title" value="{title}" type="string" />
<q:param name="genre" value="{genre}" type="string" />
<q:param name="id" value="{id}" type="integer" />
</q:query>
<q:redirect url="/" flash="Saved: {title}" />
</q:action>
<q:query name="book" datasource="db">
SELECT id, title, genre FROM books WHERE id = :id
<q:param name="id" value="{id}" type="integer" />
</q:query>
<ui:window title="Edit a book">
<q:if condition="book_result.recordCount == 0">
<ui:alert variant="danger">There is no book {id}.</ui:alert>
</q:if>
<q:else>
<!-- values= opens the form with the row's values; a one-row query works. -->
<ui:form on-submit="save" values="{book}" submit="Save" />
</q:else>
<ui:link to="/">Back</ui:link>
</ui:window>
</q:component>xml
<q:test name="the form opens on the row's page" page="/book/2">
<test:visit />
<test:expect text="Edit a book" />
<test:expect text="Save" />
<test:expect no-text="There is no book" />
</q:test>
<q:test name="saving changes only that row" page="/book/2">
<test:submit action="save" title="Libertinagem (1930)" genre="poetry" />
<test:expect redirect="/" flash="Saved: Libertinagem (1930)" />
<test:expect table="books" count="1" where="id = 2 AND title = 'Libertinagem (1930)'" />
<test:expect table="books" count="1" where="id = 1 AND title = 'Dom Casmurro'" />
<test:expect text="Libertinagem (1930) (poetry)" />
</q:test>
<q:test name="the table's rules still apply" page="/book/2">
<test:submit action="save" title="Libertinagem" genre="comic" />
<test:expect error="genre" message="Must be one of: novel, poetry, essay" />
<test:expect table="books" count="1" where="id = 2 AND genre = 'poetry'" />
</q:test>
<q:test name="a book that does not exist says so" page="/book/99">
<test:visit />
<test:expect text="There is no book 99." />
<test:expect no-text="Save" />
</q:test>text
tests/edit.test.q
PASS the form opens on the row's page
PASS saving changes only that row
PASS the table's rules still apply
PASS a book that does not exist says so
4 passed, 0 failedSee UI-10.