APIと連携しNuxt.jsで記事編集ページの実装

プログラミング
スポンサーリンク

概要

記事編集ページの作成
.

編集画面に遷移した際に
編集前の情報がformに入っている状態を作る。
.

Vue.jsもNuxt.jsも触った事がなかったので
めちゃくちゃ苦労した。笑
.

実装中の殴り書きメモはこちら。笑
鬼はまってなにしてるかわからなくなりました。

完成形

_id/edit.vue

<template>
  <v-layout justify-center :class="$style.layout">
    <v-flex :class="$style.container">
      <h2 :class="$style.title">記事編集</h2>
      <v-form ref="titleForm" @submit.prevent>
        <v-text-field
          v-model="title"
          type="text"
          name="title"
          label="タイトル"
          data-vv-name="title"
          solo
          required
          outlined
          color="#212121"
        />
      </v-form>
      <v-form ref="contentForm" @submit.prevent>
        <v-textarea
          v-model="content"
          class="textarea"
          type="text"
          rows="5"
          name="content"
          label="本文"
          hint=""
          filled
          counter
          outlined
          color="#212121"
        />
      </v-form>
      <v-btn
        color="cyan"
        elevation="4"
        ripple
        block
        depressed
        :loading="loading"
        class="white--text font-weight-bold"
        :class="[$style.button, $style.register]"
        @click="submit"
        >更新する</v-btn
      >
    </v-flex>
  </v-layout>
</template>
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
import { Context } from '@nuxt/types'

@Component({
  async fetch(context: Context) {
    const { store, error, route } = context
    const id = route.params.id
    try {
      await store.dispatch('article/fetchArticle', id)
    } catch (err) {
      error({
        statusCode: err.response.status,
        message: err.response.data.message,
      })
    }
  },
})
export default class ArticleEditPage extends Vue {
  title = this.article.title
  content = this.article.content
  loading = false
  async submit(): Promise<void> {
    this.loading = true
    const params = {
      title: this.title,
      content: this.content,
      id: this.$route.params.id,
    }
    await this.$store
      .dispatch('article/updateArticle', params)
      .then(() => {
        this.$router.push(`/articles/${this.$route.params.id}/detail`)
      })
      .finally(() => {
        this.loading = false
      })
  }

  get article() {
    return this.$store.getters['article/article']
  }
}
</script>


.

article.js(ストア)
.

export const state = () => ({
  articles: {},
  article: {},
})

export const getters = {
  articles: (state) => state.articles,
  article: (state) => state.article,
}

export const mutations = {
  setArticles(state, articles) {
    state.articles = articles
  },
  setArticle(state, article) {
    state.article = article
  },
}

export const actions = {
  async getArticles({ commit }) {
    await this.$axios.get('/v1/articles').then((response) => {
      const articles = response.data
      commit('setArticles', articles)
    })
  },
  async articleNew({ commit }, params) {
    await this.$axios.post('/v1/articles', params).then(() => {})
  },
  async updateArticle({ commit }, params) {
    await this.$axios.patch(`/v1/articles/${params.id}`, params).then(() => {})
  },
  async deleteArticle({ commit }, id) {
    await this.$axios.delete(`/v1/articles/${id}`)
  },
  async fetchArticle({ commit }, id) {
    await this.$axios.get(`/v1/articles/${id}`, id).then((response) => {
      const article = response.data
      commit('setArticle', article)
    })
  },
}


実装手順

主な手順はこちら。

http://localhost:8080/article/:id/edit

のようなルーティングにして
どの記事かわかるようにして編集したい。

  • id を取得できる vue ファイルを作る
    ファイル名をarticle/_id/editに変更

  • vue ファイルに 渡された id を取得する方法を調べ、実際に取得する
this.$route.params.id

で取得できることを確認。

  • 送られた id の記事を vue ファイルにアクセスした時点で API を叩き、データを取得する

ファイルにアクセスした時点で API を叩き、
データを取得する時はfetch を使う。
.

import { Context } from '@nuxt/types'

@Component({
  async fetch(context: Context) {
    const { store, error } = context
    try {
      await store.dispatch('article/updateArticle')
    } catch (err) {
      error({
        statusCode: err.response.status,
        message: err.response.data.message,
      })
    }
  },
})

.

こんな感じ。
.

article.js(ストア)

export const state = () => ({
  articles: {},
+  article: {},
})

export const getters = {
  articles: (state) => state.articles,
+ article: (state) => state.article,
}

export const mutations = {
  setArticles(state, articles) {
    state.articles = articles
  },
+  updateArticle(state, article) {
+    state.article = article
+  },
}

export const actions = {
  async getArticles({ commit }) {
    await this.$axios.get('/v1/articles').then((response) => {
      const articles = response.data
      commit('setArticles', articles)
    })
  },
  async articleNew({ commit }, params) {
    await this.$axios.post('/v1/articles', params).then(() => {})
  },
+  async updateArticle({ commit }, params) {
+    await this.$axios.patch('/v1/articles/:id"', params).then((response) => {
+      const article = response.data
+      commit('updateArticle', article)
    })
  },
}


fetchのdispatch先のactionを追加.
.

APIの繋ぎ込み先は
articles_controllerのupdateメソッドに。
.

成功した時のデータを変数articleにいれて
stateに入れておく。
.

export default class Articles extends Vue {
  get articles() {
    return this.$store.getters['article/articles']
  }
}

.

ここで予め用意しておいたstateの
情報をgetterを介して受け取り
変数articleに格納しておく。
.

export default class ArticleEditPage extends Vue {
  title = this.article.title
  content = this.article.content
  loading = false

.

格納したarticleはここに使用して、
遷移時にフォームに反映されるように設定。
.

export const state = () => ({
  articles: {},
  article: {},
})

export const getters = {
  articles: (state) => state.articles,
  article: (state) => state.article,
}

export const mutations = {
  setArticles(state, articles) {
    state.articles = articles
  },
  updateArticle(state, article) {
    state.article = article
  },
}

export const actions = {
  async getArticles({ commit }) {
    await this.$axios.get('/v1/articles').then((response) => {
      const articles = response.data
      commit('setArticles', articles)
    })
  },
  async articleNew({ commit }, params) {
    await this.$axios.post('/v1/articles', params).then(() => {})
  },
  async updateArticle({ commit }, params) {
    await this.$axios.patch('/v1/articles/:id', params).then((response) => {
      const article = response.data
      commit('updateArticle', article)
    })
  },
}

.

今のままだとどの記事の情報を取りに行くのか
指示ができていない。
(ここで結構ハマった)
.

https://ja.nuxtjs.org/api/context/
こちらのサイトでcontextについて調べ、
routeの情報を持っていることを確認。
.

@Component({
  async fetch(context: Context) {
    const { store, error } = context
    try {
+      await store.dispatch('article/fetchArticle', context)
-      await store.dispatch('article/fetchArticle')

こんな感じに修正。
.

しかし、
contextの中には今回必要でない情報がたくさん入っている。

.

参照https://ja.nuxtjs.org/api/context/
.

詳細情報を取得するために必要な情報はid情報のみなので
id情報のみを渡せるように修正。
.

route.params.id

とする事でvueファイルのid情報を取得できる。
.

@Component({
  async fetch(context: Context) {
-    const { store, error } = context
+    const { store, error, route } = context
+    const id = route.params.id
    try {
-     await store.dispatch('article/fetchArticle', context)
+     await store.dispatch('article/fetchArticle', id)
    } catch (err) {
      error({
        statusCode: err.response.status,
        message: err.response.data.message,
      })
    }
  },

.

actionも修正
.

+ async fetchArticle({ commit }, id) {
+    await this.$axios.get(`/v1/articles/${id}`, id).then((response) => {
      const article = response.data
      commit('setArticle', article)
    })
  },

これで意図した動きをする
実装ができた。

最後にもう一度完成形載せておきます。

完成形

_id/edit.vue

<template>
  <v-layout justify-center :class="$style.layout">
    <v-flex :class="$style.container">
      <h2 :class="$style.title">記事編集</h2>
      <v-form ref="titleForm" @submit.prevent>
        <v-text-field
          v-model="title"
          type="text"
          name="title"
          label="タイトル"
          data-vv-name="title"
          solo
          required
          outlined
          color="#212121"
        />
      </v-form>
      <v-form ref="contentForm" @submit.prevent>
        <v-textarea
          v-model="content"
          class="textarea"
          type="text"
          rows="5"
          name="content"
          label="本文"
          hint=""
          filled
          counter
          outlined
          color="#212121"
        />
      </v-form>
      <v-btn
        color="cyan"
        elevation="4"
        ripple
        block
        depressed
        :loading="loading"
        class="white--text font-weight-bold"
        :class="[$style.button, $style.register]"
        @click="submit"
        >更新する</v-btn
      >
    </v-flex>
  </v-layout>
</template>
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
import { Context } from '@nuxt/types'

@Component({
  async fetch(context: Context) {
    const { store, error, route } = context
    const id = route.params.id
    try {
      await store.dispatch('article/fetchArticle', id)
    } catch (err) {
      error({
        statusCode: err.response.status,
        message: err.response.data.message,
      })
    }
  },
})
export default class ArticleEditPage extends Vue {
  title = this.article.title
  content = this.article.content
  loading = false
  async submit(): Promise<void> {
    this.loading = true
    const params = {
      title: this.title,
      content: this.content,
      id: this.$route.params.id,
    }
    await this.$store
      .dispatch('article/updateArticle', params)
      .then(() => {
        this.$router.push(`/articles/${this.$route.params.id}/detail`)
      })
      .finally(() => {
        this.loading = false
      })
  }

  get article() {
    return this.$store.getters['article/article']
  }
}
</script>


.

article.js(ストア)
.

export const state = () => ({
  articles: {},
  article: {},
})

export const getters = {
  articles: (state) => state.articles,
  article: (state) => state.article,
}

export const mutations = {
  setArticles(state, articles) {
    state.articles = articles
  },
  setArticle(state, article) {
    state.article = article
  },
}

export const actions = {
  async getArticles({ commit }) {
    await this.$axios.get('/v1/articles').then((response) => {
      const articles = response.data
      commit('setArticles', articles)
    })
  },
  async articleNew({ commit }, params) {
    await this.$axios.post('/v1/articles', params).then(() => {})
  },
  async updateArticle({ commit }, params) {
    await this.$axios.patch(`/v1/articles/${params.id}`, params).then(() => {})
  },
  async deleteArticle({ commit }, id) {
    await this.$axios.delete(`/v1/articles/${id}`)
  },
  async fetchArticle({ commit }, id) {
    await this.$axios.get(`/v1/articles/${id}`, id).then((response) => {
      const article = response.data
      commit('setArticle', article)
    })
  },
}


コメント

タイトルとURLをコピーしました