質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.09%

[Vue.js]リストページのID要素を頼りに個別ページへ遷移させる方法

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,477

uhiyamabumi

score 16

Vue.jsで資格の過去問題解説アプリを作成したいと考えています。
リストの問題がクリックされたとき、個別の詳細ページに遷移させたいです。

DynamoDBの設定

検索に解説本文を含めてしまうとDynamoDBで多くのリソースを消費してしまうため、リスト&検索用(questionListData)と詳細用(questionDetailData)の2つのテーブルを作成しています。それぞれのデータ形式は下記の通りです。

(questionListData)

{
      "q_year" : "2012",
      "q_no" : "1",
      "category" : "english",
      "q_title" : "QUESTION_TITLE",
      "q_id" : "2012q001"
    },
    {
      "q_year" : "2012",
      "q_no" : "2",
      "category" : "english",
      "q_title" : "QUESTION_TITLE",
      "q_id" : "2012q002",
    },

(questionDetailData)

{
      "q_id" : "2012q001",
      "q_body" : "QUESTION_BODY",
      "q_desc" : "QUESTION_DESCRIPTON",
      "image_url" : "",
      "image_alt" : ""
    },
    {
      "q_id" : "2012q002",
      "q_body" : "QUESTION_BODY",
      "q_desc" : "QUESTION_DESCRIPTION",
      "image_url" : "",
      "image_alt" : ""
    },

上記のデータをBoto3を使って、それぞれのDynamoDBのテーブルに格納しています。Serverlessフレームワークのofflineプラグインを使って、ローカル環境でAPI Gatewayを実行しています。Postmanでそれぞれのエンドポイントから適切にデータが取得できることを確認しています。

例えば、「http://localhost:3000/questions/2012」とすると、2012年の全ての問題が取得できます。同様に「http://localhost:3000/question/2012q001」とすると、2012年1問目の問題が取得できます。

実現したいのは、リストのq_idを詳細用の「http://localhost:3000/question/{q_id}」に当てはめて、リストページからそれぞれの個別ページに遷移させることです。

Vuejsの設定

現在のVueの構造と希望の形を簡単なイメージにしてみました。
Vue構造と希望

まずリスト用データはRootInstance(main.js)でAxiosを使って読み込んでいます。これをdataでquestionsとしてOverview、QuestionListと下層に流しています。

import Vue from 'vue'
import VueRouter from 'vue-router';
import Axios from 'axios';

Vue.use(VueRouter);
Vue.use(Axios);

import routes from './util/routes';
const router = new VueRouter({routes});

new Vue({
  el: '#app',
  data: {
    questions: []
  },
  created() {
    Axios.get('http://localhost:3000/questions/2012').then(response => {
      this.questions = response.data
    });
  },
  router
});


QuestionList.vueでは下記のようにして、2012年度分のデータを表示することができました。一応<router-link>で囲っていますが、当然詳細ページに遷移することはできません。

<template>
  <div class="list">
    <h1>QuestionList page.</h1>
    <div v-for="question in questions" v-bind:question="question.question">
      <router-link>
        <h2>{{ question.q_title }}</h2>
      </router-link>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'QuestionList',
    props: ['questions']
  }
</script>

Axiosの公式ドキュメントなどから、paramsにq_idを指定して下記のような形になるのかなーと想像しますが、まだ具体的な解決ができずにいます。

axios.get('http://localhost:3000/question/', {
        params: {
            id: question.q_id
        }
      })

リストデータを読み込む階層など、改善の余地は多々あるかと思いますが、「リストページからq_idを頼りに詳細ページへ遷移する方法」を教えて頂ければなと思います。

有用そうなソースも歓迎です。

[2017/08/19追記 ページ遷移できました]

aro10さんの指摘に沿って次のように修正し、ページを遷移させることができました。

Vue-RouterのPath設定に誤りがありました。

(修正前) { path: '/question/:id', component: QuestionDetail, name: 'detail'}
(修正後) { path: '/question/:q_id', component: QuestionDetail, name: 'detail'}

上記のようなルート定義を作成したうえで、QuestionList.vueのrouter-link部分を下記のように修正したところ、無事ページ遷移させることができました。

<template>
  <div class="list">
    <h1>QuestionList page.</h1>
    <div v-for="question in questions" v-bind:question="question.question">
      <router-link :to="{ name: 'detail', params: {q_id: question.q_id}}">
        <h2>{{ question.q_title }}</h2>
      </router-link>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'QuestionList',
    props: ['questions']
  }
</script>

ページ遷移と同時に詳細用API(/question/{q_id})を発火したい

アドバイスによりページ遷移が完了しましたが、現状では詳細情報を取得するためのAPI(http://localhost:3000/question/{q_id})が未発火なので、遷移先のページが空白のままです。

RootInstanceのcreated()で2012年の問題群を取得したように、ページ遷移したと同時にq_idを上記APIの{q_id}に代入して個別問題を取得したいと考えているのですが、どのような方法が考えられるでしょうか。動作こそしないものの、現在のDetailのコードなども早めに追記しようと思います。

※動作しないので参考にしないでください。
とりあえず詳細データのq_bodyを表示させたいと考えています。現状は詳細情報が取得されておらず、questionが空の状態なので、<div v-else>が実行されて、ページに「No Page.」と表示だけされています。

<template>
  <div class="detail">
    <div v-if="question != null">
      <h2>{{ this.question.q_body }}</h2>
    </div>

    <div v-else>
      <p>No Page.</p>
    </div>
  </div>
</template>

<script>

  export default {
    name: 'detail',
    props: ['questions'],
    created() {
      axios.get('http://localhost:3000/question/{q_id}', {
        params: {
          q_id: question.question.q_id
        }
      })
        .then(function(response) {
          console.log(response);
          this.question = response.data
        })
        .catch(function(response) {
          console.log(error)
        });
    }
  }
</script>

詳細ページの編集中、リストから受け取ったq_idが組み込めず…

this.$route.params.q_idという便利なアクセス手法があるとのこと。
これを利用してAPIの{q_id}に値を組み込めば簡単に行くのではないかと思ったものの、苦戦しています。Vue Devtoolを見る限り、詳細ページは確かにリストからq_idのパラメータを受け取っているものの、活用することがなかなかできません。

少し理解が追いついていない現状ですが、現在のコードは下記のようになっています。

(詳細用ページ)

<template>
  <div class="detail">
    <div v-if="question != null">
      <h2>{{ question.q_body }}</h2>
    </div>

    <div v-else>
      <p>No Page.</p>
    </div>
  </div>
</template>

<script>
  import axios from 'axios';

  export default {
    name: 'detail',
    data() {
        return {
          question: null
        }
    },
    mounted() {
      this.getQuestion(this.q_id).then(question => this.question = question);
    },
    beforeRouteUpdate(to, from ,next) {
      this.getQuestion(to.params.q_id).then(question => this.question = question);
      next();
    },
    methods: {
      getQuestion(q_id) {
        return axios.get("http://localhost:3000/question/", {
          params: {
            q_id: this.$route.params.q_id
          }
        })
          .then(function (response) {
            question = response.data
          })
          .then(function (error) {
            console.log("http request denied");
          });
      }
    }
  }
</script>


※まだ使い方をよく理解してはいませんが、beforeRouteUpdateがwatchと同じような働きをするとのことなので、参考にしたサイトに習ってそのまま取り入れています。

一応上記のコードを実行するとコンソールに

GET http://localhost:3000/question/?q_id=h28s002 404 (Not Found)

と表示されるので、「惜しいのかな?」とも考えています。
Axiosのドキュメントを参考にしたparamsの指定では、APIのURLと違ってしまうので、この点を上手く修正すれば、とりあえずAPIを上手く発火できそうな気がします。(もっとシンプルな方法がある気がしてならないけど…)

コードからもわかるとおり混乱中です。修正を続けていますが、具体的な修正案などがあればありがたいです。「リストから取得したq_idを、詳細情報取得用のAPIに組み込んで、その内容を表示する」ということがしたいです。
よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

Vue.jsで一覧ページからidをキーとした詳細ページへ遷移したいという要件であれば、
Vue-Routerで対応するルート定義を作って、

#例
{ path: '/question/:q_id', component: DetailXXX }


Vue-Router 動的ルートマッチング

router-linkのtoのparamsをリストの各要素のidなどにしてあげれば多分動くので行けると思います。

#例
<router-link :to="{ name: 'question', params: { q_id: question.q_id }}">IDが{{question.q_id}}の詳細へ</router-link>


Vue-Router router-link

※最後のAxiosの例で動的なHTMLかVueテンプレートを取得した後、そのHTMLをそのまま詳細ページとして当てはめて遷移するといった要件であるならば、もう少し複雑な事が必要かもしれません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/19 02:38

    ありがとうございます。routerのpath設定が間違っていたようで、御指摘に沿って修正したところ、無事ページを遷移させることができました。本文に追記しておきます。

    ページの遷移を完了させることができましたが、詳細情報を取得するためのAPI(http://localhost:3000/question/{q_id})が未発火なので、遷移先のページが空白のままです。

    RootInstanceのcreated()で2012年の問題群を取得したように、ページ遷移したと同時にq_idを上記APIの{q_id}に代入して個別問題を取得したいと考えているのですが、どのような方法が考えられるでしょうか。動作こそしないものの、現在のDetailのコードなども早めに追記しようと思います。

    もし解決案がありましたら、また御指摘を頂きたいと思います。よろしくお願いします。

    キャンセル

  • 2017/08/19 02:53 編集

    詳細ページでページ情報を表示するのに必要なデータを取得するAPIを呼び出すタイミングは、mountedとwatchが良いかと思います。
    ```
    mouted:{ //コンポーネントが作成された際に発動
    //APIからデータ取得し、ViewModelのdata等に適応
    },
    watch: {
    '$route' (to, from) { //同一コンポーネント間で遷移が発生した場合に発動
    //APIからデータ取得し、ViewModelのdata等に適応
    }
    }
    ```
    [動的ルートマッチング](https://router.vuejs.org/ja/essentials/dynamic-matching.html)

    q_idの値は、this.$route.params.q_id のような形で取得できます
    [ルートオブジェクト](https://router.vuejs.org/ja/api/route-object.html)

    キャンセル

  • 2017/08/20 03:15

    有用な情報をありがとうございます。
    this.$route.params.q_idをAPIの{q_id}部分に組み込もうと、しばらく試行錯誤してみたのですが、少し苦戦しています。Vue Devtoolを見る限り、paramsはきちんとリストから読み込めているようです。
    少しめちゃくちゃですが、現状のコードを再び追記しようと思います。具体的な修正案などがもしあれば、またアドバイスを頂ければなと思います。よろしくお願いします。

    キャンセル

  • 2017/08/20 12:17

    質問文が少し長くなってしまったので、新しい質問として再度投稿し直そうと思います。丁寧に返答を下さりありがとうございました。ベストアンサーにさせて頂きます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.09%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる