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

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

ただいまの
回答率

89.13%

【Rails 5.1.6 + Vue.js】新規登録画面への遷移について

受付中

回答 0

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 560

tenten11055

score 56

現状起きていること

RailsとVue.jsでCURDを備えたアプリを作っていたのですが、
新規登録画面を作成中に問題が起こりました。

一覧画面から新規登録画面(localhost:3000/#/projects/new)に遷移すると、
URLは正しく表示されているのですが画面が詳細画面(/projects/:id)になってしまいます。
もちろんこの時IDを取得するメソッドは書いていないので、項目名しか表示されません。

またその画面でコンソールを開くと下記のエラーがでていました。

xhr.js:166 GET http://localhost:3000/api/projects/new.json 404 (Not Found)
createError.js:17 Uncaught (in promise) Error: Request failed with status code 404
    at createError (createError.js:17)
    at settle (settle.js:19)
    at XMLHttpRequest.handleLoad (xhr.js:60)


xhr.js:166 GET http://localhost:3000/api/projects/new.json 404 (Not Found)
こちらに関して、なぜGETパラメータになっていて、なぜnew.jsonを呼ぼうとしているのかが分かりません。
そもそもnew.jsonというものはないのでエラーになって当然なのです。

またボタンからの遷移、URL直打ち両方で同様のエラーに遭遇します。

解決したいこと

詳細画面ではなく、ちゃんと新規登録画面に遷移できるようにしたい。

関与しそうなソース

projects.vue(一覧画面 ちゃんと開ける)

<template>
  <div id = 'project'>
    <h1>プロジェクト一覧</h1>
    <div class = 'new-link-btn'>
      <router-link to="/projects/new" class='btn waves-effect waves-light'><i class="material-icons left">cloud</i>新規登録</router-link>
    </div>
    <table>
      <tbody>
        <tr>
          <th>プロジェクト名</th>
          <th>担当部署</th>
          <th>売上金額</th>
          <th>開始日</th>
          <th>終了日</th>
          <th></th>
        </tr>

        <tr v-for="project in projects" :key="project.id">
          <td>{{ project.project_name }}</td>
          <td>{{ project.department }}</td>
          <td>{{ project.amount }}</td>
          <td>{{ project.start_date }}</td>
          <td>{{ project.end_date }}</td>
          <th><router-link :to="{name: 'Project', params: {id: project.id}}" class="btn btn-info">詳細</router-link></th>
        </tr>
      </tbody>
    </table>
  </div>
</template>


<script>
  import axios from 'axios'

  export default {
    data: function() {
      return {
        projects: []
      }
    },
    mounted: function() {
      this.fetchProjects();
    },
    methods: {
      fetchProjects: function() {
        axios.get('api/projects.json').then(response => (this.projects = response.data));
      }
    }
  }
</script>

<style scoped>
  p {
    font-size: 2em;
    text-align: center;
  }

  h1 {
    font-size: 40px;
    text-align: center;
    padding-bottom: 20px;
  }

  .new-link-btn {
    text-align: end;
  }
</style>

project.vue(詳細画面 ちゃんと開ける)

<template>
  <div id = 'project-show'>
    <dl>
      <dt>プロジェクト名</dt>
      <dd>{{ project.project_name }}</dd>
      <dt>担当部署</dt>
      <dd>{{ project.department }}</dd>
      <dt>売上金額</dt>
      <dd>{{ project.amount }}</dd>
      <dt>開始日</dt>
      <dd>{{ project.start_date }}</dd>
      <dt>終了日</dt>
      <dd>{{ project.end_date }}</dd>
    </dl>
    <div class = 'history-back-btn'>
      <a @click="$router.go(-1)" class="btn">戻る</a>
    </div>
  </div>
</template>


<script>
import axios from 'axios';

export default {
  data: function() {
    return {
      project: {}
    }
  },
  mounted: function() {
    this.fetchProject();
  },
  methods: {
    fetchProject: function() {
      axios.get(`/api/projects/${this.$route.params.id}.json`)
      .then(response => (this.project = response.data))
    }
  }
}
</script>

<style scoped>
</style>

create_project.vue(新規登録画面 問題の画面)

<template>
  <form @submit.prevent="createProject">
    <div v-if="errors.length != 0">
      <ul v-for="error in errors" :key="error">
        <li><font color="red">{{ error }}</font></li>
      </ul>
    </div>
    <div>
      <label>プロジェクト名</label>
      <input v-model="project.name" type="text">
    </div>
      <label>担当部署</label>
      <select v-model="project.department">
        <option>開発</option>
        <option>デザイン</option>
        <option>マーケティング</option>
        <option>その他</option>
      </select>
    <div>
      <label>売上金額</label>
      <input v-model="project.amount" type="number" min="0" max="999999999">
    </div>
    <div>
      <label>開始日</label>
      <input v-model="project.start_date" type="date">
    </div>
    <div>
      <label>終了日</label>
      <input v-model="project.end_date" type="date">
    </div>

    <button type="submit" class="btn btn-submit">登録</button>
  </form>
</template>

<script>
  import axios from 'axios';

  export default {
    data: function() {
      return {
        project: {
          project_name: '',
          department: '',
          amount: '',
          start_date: '',
          end_date: ''
        },
        errors: ''
      }
    },
    methods: {
      createProject: function() {
        axios.post('/api/projects', this.project)
        .then(response => {
          let project = response.data;
          this.$router.push({ name: 'Project', params: { id: project.id } });
        })
        .catch(error => {
          console.error(error);
          if(error.response.data && error.response.data.errors) {
            this.errors = error.response.data.errors
          }
        });
      }
    }
  }
</script>

<style scoped>
</style>

router.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from '../components/index.vue'
import About from '../components/about.vue'
import Contact from '../components/contact.vue'
import Projects from '../components/projects/projects.vue' <- 一覧画面
import Project from '../components/projects/project.vue'  <- 詳細画面
import CreateProject from '../components/projects/create_project.vue' <- 新規登録画面


Vue.use(VueRouter);
  // mode: 'history',
  const routes = [
    { path: '/', component: Index },
    { path: '/about', component: About },
    { path: '/contact', component: Contact },
    { path: '/projects', component: Projects, name: 'Projects' },
    { path: '/projects/:id', component: Project, name: 'Project' },
    { path: '/projects/new', component: CreateProject, name: 'CreateProject' }
  ];

  export default new VueRouter({ routes })

controllers/api/projects_controller.rb

class Api::ProjectsController < ApplicationController

  before_action :set_project, only: [:show]

  # 拾えなかったExceptionが発生したら500 Internal server errorを応答する
  rescue_from Exception, with: :render_status_500

  # ActiveRecordのレコードが見つからなければ404 not foundを応答する
  rescue_from ActiveRecord::RecordNotFound, with: :render_status_404


  def index
    projects = Project.all
    render json: projects
  end

  def show
    render json: @project
  end

  def create
    project = Project.new(project_params)
    if project.save
      render json: project, status: :created
    else
      render json: { errors: project.errors.full_messages }, status: :unprocessable_entity
    end
  end

  private

  def set_project
    @project = Project.find(params[:id])
  end

  def project_params
    params.fetch(:project, {}).permit(:project_name, :department, :amount, :start_date, :end_date)
  end

  def render_status_404(exception)
    render json: { errors: [exception] }, status: 404
  end

  def render_status_500(exception)
    render json: { errors: [exception] }, status: 500
  end


end

routes.rb

Rails.application.routes.draw do
  # get 'home/index'

  root to:"home#index"
  get '/projects', to: 'home#index'
  get '/projects/:id', to: 'home#index'
  get '/projects/new', to: 'home#index'

  namespace :api, format: 'json' do
    resources :tasks, only: [:index, :create, :update]
    resources :projects, only: [:index, :show, :create]
  end

end

javascript/packs/todo.js(viewやコンポーネントを定義しているjsファイル)

import Vue from 'vue'
import Router from './router/router'
import Header from './components/header.vue'

var app = new Vue({
  router: Router,
  el: '#app',
  components: {
    'navbar': Header,
  }
});

views/home/index.html.erb

<div id="app">
  <navbar></navbar>

  <div class = 'container'>
      <router-view></router-view>
  </div>

</div>

<%= javascript_pack_tag 'todo' %>

宜しくお願い致します。

2019.11.21 追記

router-linkを名前付きルートにしたところうまくいきました。

projects.vue

ここを

<router-link to="/projects/new" class='btn waves-effect waves-light'><i class="material-icons left">cloud</i>新規登録</router-link>


こうした

<router-link :to="{ name: 'CreateProject' }" class='btn waves-effect waves-light'><i class="material-icons left">cloud</i>新規登録</router-link>

しかし、URL直打ちで画面遷移しようとすると詳細画面に行ってしまいます。
なぜこのような現象が起こるのか、不明のままです。
ご回答募集しております。。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

まだ回答がついていません

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

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