題目
任務目標:實作一個「留言板」功能。
- 在
src/actions/index.ts中,增加一個新的 Action 叫做postComment。 - 這個 Action 需要接收
author(字串) 和content(字串)。 - 在
handler中,將收到的資料console.log出來(模擬存入資料庫),並回傳「留言成功」。 - 在
about.astro建立一個簡單的表單,讓使用者輸入名字與內容,並在提交後用alert()顯示伺服器的回傳訊息。 - 進階挑戰: 嘗試在 Action 的
input中使用z.string().min(5),看看如果你輸入少於 5 個字的內容,前端會收到什麼樣的錯誤訊息?
目前專案結構
📁 .astro/
📁 .vscode/
📁 node_modules/
📁 public/
└─ favicon.svg
📁 src/
├─ 📁 actions/
│ └─ index.ts
├─ 📁 components/
│ ├─ DescriptionControl.jsx
│ ├─ LikeButton.jsx
│ ├─ PostsQuote.astro
│ ├─ RandomQuote.astro
│ └─ ServerTime.astro
├─ 📁 content/
│ ├─ 📁 blog/
│ │ └─ post-1.md
│ └─ config.ts
├─ 📁 layouts/
│ └─ BaseLayout.astro
└─ 📁 pages/
├─ 📁 posts/
│ └─ [id].astro
├─ about.astro
├─ blog.astro
└─ index.astro
.gitignore
astro.config.mjs
package-lock.json
package.json
README.md
tscon
1_修改 src/actions/index.ts
import { defineAction } from "astro:actions";
import { z } from "astro:schema";
export const server = {
getGreeting: defineAction({
input: z.object({
name: z.string()
}),
handler: async (input) => {
return `你好 ${input.name},這是來自伺服器的回覆!`;
}
}),
postComment: defineAction({
input: z.object({
author: z.string(),
content: z.string(),
}),
handler: async (input) => {
console.log(input);
return "留言成功";
}
}),
}
2_修改 src/pages/about.astro
---
import BaseLayout from '../layouts/BaseLayout.astro'
import DescriptionControl from '../components/DescriptionControl'
import RandomQuote from '../components/RandomQuote.astro'
const pageTitle = '關於我'
const identity = {
firstName: '小明',
hobbies: ['reading', 'gaming', 'coding'],
}
const description =
'這段文字旨在模擬實際使用中的描述欄位,長度超過二十個字以符合需求。'
---
<BaseLayout pageTitle={pageTitle} pageName="about">
<h2>{identity.firstName}</h2>
<h3>我的愛好</h3>
<ul>
{identity.hobbies.map((x) => <li>{x}</li>)}
</ul>
<h3>描述</h3>
<DescriptionControl description={description} client:visible />
<h3>隨機生成</h3>
<RandomQuote server:defer>
<p slot="fallback" class="loading">等待隨機生成....</p>
</RandomQuote>
<form id="form">
<input
type="text"
name="author"
id="author"
placeholder="使用者名稱"
required="required"
/>
<input
type="text"
name="content"
id="content"
placeholder="輸入留言"
required="required"
/>
<button type="submit">發送</button>
</form>
<script>
import { actions } from 'astro:actions'
const formElement = document.querySelector('form')
formElement?.addEventListener('submit', async (e) => {
e.preventDefault()
const formData = new FormData(formElement)
const author = formData.get('author') as string
const content = formData.get('content') as string
const { data, error } = await actions.postComment({ author, content })
if (!error) {
alert(data)
} else {
alert(error)
}
})
</script>
</BaseLayout>
3_進階挑戰
3.1_修改 src/actions/index.ts
import { defineAction } from "astro:actions";
import { z } from "astro:schema";
export const server = {
getGreeting: defineAction({
input: z.object({
name: z.string()
}),
handler: async (input) => {
return `你好 ${input.name},這是來自伺服器的回覆!`;
}
}),
postComment: defineAction({
input: z.object({
author: z.string(),
content: z.string().min(5), // 修改這裡
}),
handler: async (input) => {
console.log(input);
return "留言成功";
}
}),
}
