今回の記事ではデータの追加から詳細の表示までを実装します。
前回の記事はこちら: Rails + Nuxt CRUDアプリ作成(一覧表示編)
1. 新規作成ボタンの作成(Nuxt)
まずは一覧画面に新規作成ボタンを作成します。
front/pages/posts/index.vue
<template>
<div class="container py-5 position-relative"><!-- 追加 -->
<b-card
v-for="post in posts"
:key="post.id"
>
<b-card-text>
{{ post.content }}
</b-card-text>
</b-card>
<!-- ここから -->
<b-button
v-b-modal.new-modal
class="position-absolute mt-4 action-btn"
pill
variant="primary"
size="lg"
>
+
</b-button>
<b-modal
hide-header
hide-footer
id="new-modal"
>
<b-form-textarea
v-model="content"
autofocus
/>
<b-button
class="mt-3"
variant="primary"
@click="save()"
>
保存
</b-button>
</b-modal>
<!-- ここまで -->
</div>
</template>
....
<style scoped>
.action-btn {
right: 16px;
}
</style>
これで画面の右下にボタンが表示されそれをクリックすると下記のようなモーダル が表示されるようになりました。
保存ボタンを押すとsave()メソッドが実行されますが、今はまだ定義していないのでエラーになります。

v-b-modalの記述はbootstrapの記述なので今回は説明を省略します。
気になるかたは「bootstrap-vue」のmodalの箇所をみてください。
https://bootstrap-vue.org/docs/components/modal#modals
2. axios通信: 送信(Nuxt)
次にモーダル の「保存」ボタンをクリックした時にRailsのcreateアクションを実行するように実装していきます。
front/pages/posts/index.vue
<script>
export default {
...省略
computed: {
params() {
return {
post: {
content: this.content
}
}
}
},
methods: {
save() {
const url = "/api/v1/posts"
this.$axios.post(url, this.params)
.then((res) => {
// 保存成功時
})
.catch((err) => {
// 保存失敗時
})
}
}
}
一覧で取得したときのようにaxios通信を行います。
変更点は以下の2つ
- 新規作成はサーバーにデータを送る必要があるのでHTTPリクエストはPOST
- 引数にパラメータを追加
this.params
は computed
で定義したparamsなので中身は { post: { content: { this.content } } } が入っています。 this.content
は data
で定義したcontentなのでモーダル のtextaraで入力したデータが入ります。
これで「保存」を押した時に
http://localhost:3000/api/v1/posts のPOSTリクエストを送りパラメータにはthis.paramsが入ります。
次はRailsの/api/v1/postsのPOSTリクエスト すなわちPostsコントローラのcreateアクションを実装していきます。
3. createアクション実装(Rails)
では、PostsControllerのcreateアクションを実装していきます。indexアクションを実装したように下記の手順で実装します。
- ルーティングの生成
- createアクションの実装
成功したかのメッセージを返してあげればいいので、jbuilderの実装は不要です。
- ルーティングの生成
api/config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :hello, only: [:index]
resources :posts, only: [:index, :create]
end
end
end
- createアクションの実装
api/app/controllers/api/v1/posts_controller.rb
... 省略
def create
post = Post.new(content: params[:post][:content])
if post.save
render json: '作成に成功しました', status: 200
else
render json: '作成に失敗しました', status: 500
end
end
作成の可否でstatusを分けてjsonでメッセージを返してあげます。
これでcreateアクションを実装できたので次はNuxt側のレスポンスの実装を行います。
4. axios通信: レスポンス(Nuxt)
save() {
const url = "/api/v1/posts"
this.$axios.post(url, this.params)
.then((res) => {
// 保存成功時
console.log(res)
})
.catch((err) => {
// 保存失敗時
})
console.log()で中身を確認します。下記の画像のようにレスポンスが返ってきたら成功です。

ただ今のままだと保存に成功したのかがわかりにくいので保存成功時に下記の処理を加えます。
- 成功メッセージのトーストを表示する
fetchContents()
メソッドを実行する
後、作成に失敗した時の処理もcatchの中に記述します。
さらにcontentは空では「保存」ボタンを入力できないようにdisabledプロパティを加えます。
完成コードはこちら
front/pages/posts/index.vue
<template>
<div class="container py-5 position-relative">
<b-card
v-for="post in posts"
:key="post.id"
>
<b-card-text>
{{ post.content }}
</b-card-text>
</b-card>
<b-button
v-b-modal.new-modal
class="position-absolute mt-4 action-btn"
pill
variant="primary"
size="lg"
>
+
</b-button>
<b-modal
hide-header
hide-footer
id="new-modal"
>
<b-form-textarea
v-model="content"
autofocus
/>
<b-button
class="mt-3"
variant="primary"
:disabled="disabled"
@click="save()"
>
保存
</b-button>
</b-modal>
</div>
</template>
<script>
export default {
data: () => {
return {
posts: [],
}
},
mounted() {
this.fetchContents()
},
computed: {
disabled() {
return this.content.length === 0
},
params() {
return {
post: {
content: this.content
}
}
}
},
methods: {
fetchContents() {
const url = "/api/v1/posts"
this.$axios.get(url)
.then((res) => {
this.posts = res.data.posts
})
},
save() {
// 保存処理
const url = "/api/v1/posts"
this.$axios.post(url, this.params)
.then((res) => {
console.log(res)
this.$bvModal.hide('new-modal')
this.content = ''
this.fetchContents()
this.$bvToast.toast(res.data, {
title: '成功',
variant: 'success'
})
})
.catch((err) => {
const message = err.response.data
this.$bvToast.toast(message, {
title: 'エラー',
variant: 'danger'
})
})
},
}
}
</script>
<style scoped>
.action-btn {
right: 16px;
}
</style>
これで新規登録ボタンからフォームに入力して保存までを実装できました。
次は詳細画面を作成していきます。
5. 詳細画面作成(Nuxt)
ここからは一覧画面で表示される要素をクリックしたら詳細ページに遷移するように実装していきます。
template
- classの追加
- クリックイベントを追加
script
- クリックイベントの実装
front/pages/posts/index.vue
<template>
<div class="container py-5 position-relative">
<b-card
v-for="post in posts"
:key="post.id"
class="cursor-pointer"
@click="toShow(post.id)"
>
<b-card-text>
{{ post.content }}
</b-card-text>
</b-card>
...省略
methods: {
toShow(id) {
this.$router.push(`/posts/${id}`)
}
}
これで要素にカーソルを合わせるとポインターになり、クリックするとURLが http://localhost:8080/posts/id
←idは作成したpostのid
に変更しました。まだ詳細ページは実装していないのでエラーになります。
詳細画面のファイル名は _id.vue
になります。詳しくはこちらの公式ページに記載されています。
動的なルーティング: https://ja.nuxtjs.org/docs/2.x/features/file-system-routing/
front/pages/posts 配下に _id.vue
ファイルを作成します。
front/pages/posts/_id.vue
<template>
<div class="container py-5">
<b-card>
<b-card-text>
詳細画面
</b-card-text>
<b-button
size="sm"
@click="toTop()"
>
Topへ
</b-button>
</b-card>
</div>
</template>
<script>
export default {
toTop() {
this.$router.push('/posts')
},
}
}
</script>
これで一覧画面から要素をクリックした時に詳細画面に遷移できました。
次は詳細画面でaxios通信で単一レコードの表示を実装します。
6. axios通信(Nuxt)
先ほど作成した詳細ページにaxios通信の処理を加えていきます。
内容は一覧画面同様で今回は全てのレコードではなくurlのidの箇所レコードのみを取得します。
front/pages/posts/_id.vue
<template>
<div class="container py-5">
<b-card>
<b-card-text>
{{ post.content }}
</b-card-text>
...省略
export default {
data: () => {
return {
post: {},
}
},
mounted() {
this.fetchContent()
},
methods: {
fetchContent() {
const url = `/api/v1/posts/${this.$route.params.id}`
this.$axios.get(url)
.then((res) => {
this.post = res.data.post
})
.catch(() => {
this.toTop()
})
},
urlのid取得は今回は $route.params.id
で取得できます。
これで詳細画面に遷移時axios通信をするようになったのでRailsのshowアクションを実装していきます。
7. showアクション実装(Rails)
一覧画面と同じように
- ルーティングの生成
- showアクションの実装
- jbuilderで返すデータを整形する
の順序で実装していきます。
api/config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :hello, only: [:index]
resources :posts, only: [:index, :create, :show]
end
end
end
api/app/contollers/api/v1/posts/controller.rb
def show
@post = Post.find_by(id: params[:id])
unless @post
render json: @post, status: 500
end
end
api/app/views/api/v1/posts/show.jbuilder
json.post do
json.extract! @post, :id, :content, :updated_at
end
これでRails側のshowアクションを実装できました。
もう詳細画面をリロードし、下記画像のようにPostデータを取得できれば完成です。

次の記事はコンテンツの編集、削除を実装していきます。