Формы: отправка данных на сервер
Чтобы добавить статью, страница должна отправить данные формы в API. Данные формы отправляются по действию пользователя, поэтому здесь нужен не useFetch, а $fetch с методом POST. Сервер для этого уже готов: обработчик server/api/posts.post.ts из главы о серверной части.
Страница новой статьи
npx nuxi add page posts/newЗамените содержимое app/pages/posts/new.vue:
<!-- app/pages/posts/new.vue -->
<script setup lang="ts">
const form = reactive({ title: '', text: '' })
async function submit() {
const post = await $fetch('/api/posts', {
method: 'POST',
body: form
})
await navigateTo(`/posts/${post.id}`)
}
</script>
<template>
<form @submit.prevent="submit">
<h2>Новая статья</h2>
<input v-model="form.title" placeholder="Заголовок">
<textarea v-model="form.text" placeholder="Текст" />
<button>Опубликовать</button>
</form>
</template>$fetch отправляет объект form в теле запроса в формате JSON. Сервер возвращает созданную статью, и navigateTo открывает её страницу. Заполните форму на /posts/new, нажмите «Опубликовать» — откроется новая статья. Добавьте в меню макета ссылку <NuxtLink to="/posts/new">, чтобы попадать на форму.
Ошибки и состояние отправки
Сервер отвечает кодом 400, если поле пустое. $fetch в этом случае бросает ошибку, а текст ответа лежит в её поле data:
<!-- app/pages/posts/new.vue -->
<script setup lang="ts">
const form = reactive({ title: '', text: '' })
const errorMessage = ref('')
const sending = ref(false)
async function submit() {
sending.value = true
errorMessage.value = ''
try {
const post = await $fetch('/api/posts', { method: 'POST', body: form })
await navigateTo(`/posts/${post.id}`)
} catch (e) {
const err = e as { data?: { message?: string } }
errorMessage.value = err.data?.message ?? 'Не удалось сохранить статью'
} finally {
sending.value = false
}
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="form.title" placeholder="Заголовок">
<textarea v-model="form.text" placeholder="Текст" />
<p v-if="errorMessage">{{ errorMessage }}</p>
<button :disabled="sending">Опубликовать</button>
</form>
</template>Отправьте форму с пустым текстом — под полями появится «Заполните заголовок и текст» от сервера. Пока запрос идёт, кнопка выключена, и статья не отправится дважды.
Форма комментария
Создайте компонент app/components/post/CommentForm.vue:
<!-- app/components/post/CommentForm.vue -->
<script setup lang="ts">
const props = defineProps<{ postId: number }>()
const emit = defineEmits<{ added: [] }>()
const author = ref('')
const text = ref('')
async function send() {
await $fetch(`/api/posts/${props.postId}/comments`, {
method: 'POST',
body: { author: author.value, text: text.value }
})
text.value = ''
emit('added')
}
</script>
<template>
<form @submit.prevent="send">
<input v-model="author" placeholder="Ваше имя">
<textarea v-model="text" placeholder="Комментарий" />
<button>Отправить</button>
</form>
</template>После отправки компонент сообщает родителю событием added. На странице статьи, где данные загружает useAsyncData, подключите форму и обновление:
<!-- app/pages/posts/[id].vue -->
<PostCommentForm v-if="data" :post-id="data.post.id" @added="refresh()" />refresh возьмите из результата: const { data, refresh } = await useAsyncData(...). Новый комментарий появится под статьёй сразу после отправки.
Частая ошибка
Вызвать useFetch внутри обработчика submit. Composables вроде useFetch предназначены для <script setup>: внутри обработчика события они работают ненадёжно и выводят предупреждение. Для запросов по нажатию кнопки или отправке формы используйте $fetch.