ChatGPTとGitHub Copilotを活用しながらWebアプリケーションを作る【Vue.jsのルーティング追加】

以下の記事の続きです。

programming-gogogogo.hatenablog.com

今回はVue.jsのルーティングを追加したいと思います。

ChatGPTにルーティングを追加する方法を質問してみました。

入力

I have a Vue.js application and in user list page I already wrote below code which shows all users fetched from DB.

<ul>
    <li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>

And, I want to add the link to each user name displayed in browser so that can be navigate to the users detailed page which has users/:user_id. What should I add?

出力

To add a link to each user name displayed in the list and navigate to their detailed page, you can use the Vue.js router. Here's an example of how you can modify your code to achieve this:

<ul>
  <li v-for="user in users" :key="user.id">
    <router-link :to="'/users/' + user.id">{{ user.name }}</router-link>
  </li>
</ul>

In the above code, we're using the router-link component provided by Vue.js to create a link to the user's detailed page. The to attribute of router-link is bound to a computed property that concatenates the /users/ string with the user's id property.

To make this work, you'll need to configure your Vue.js router to handle the /users/:user_id route and load the user's detailed page component. Here's an example of how you can do that:

// Import the Vue.js and Vue Router libraries
import Vue from 'vue'
import VueRouter from 'vue-router'

// Import the user's detailed page component
import UserDetail from './components/UserDetail.vue'

// Register the Vue.js router plugin
Vue.use(VueRouter)

// Create a new Vue.js router instance
const router = new VueRouter({
  routes: [
    // Define the /users/:user_id route and load the UserDetail component
    { path: '/users/:user_id', component: UserDetail },
  ],
})

// Mount the Vue.js app with the router
new Vue({
  router,
  // ...
}).$mount('#app')

In the above code, we're defining a new route for the /users/:user_id path and loading the UserDetail component when this route is matched. Note that you'll need to replace UserDetail with the actual name of your user's detailed page component.

With this code, when the user clicks on a user's name in the list, they'll be navigated to the corresponding detailed page. The user's id will be passed as a route parameter and can be accessed in the UserDetail component via this.$route.params.user_id.

上記の回答にあるコードを試してみるとhttp://localhost:8080/users/1にルーティングしてほしい時にhttp://localhost:8080/users/users/1へルーティングされてしまいました.

再度質問してみました。

入力

Thanks. When I try above code and push the link on user name, the app navigated me to http://localhost:8080/users/users/1 but I want http://localhost:8080/users/1. Could you fix that?

出力

Sure, to fix that you need to remove the extra "users" from your link. You can do this by changing the to property of the <router-link> component to the following:

<router-link :to="`/users/${user.id}`">{{ user.name }}</router-link>

This will create a link with the path /users/:user_id as you wanted, without the extra "users" segment.

上記のコードを試しても想定したルーティングにはならず、この後2、3回質問してもあまり良い答えは得られませんでした。 そこで改めて自分が解きたい問題を整理してみました。

Vue.jsでユーザー一覧はv-forで表示できていて、それぞれIDを持っている。 ↓ Vue.jsのtemplateの中でv-forでループしているところに、各ユーザーのIDを変数で持たせるrouter-linkを追加したいが、書き方がわからない。

という状況なので、問題を最小単位まで分解すると「Vue Routerでの変数の使い方がわからない」ということになると思います。

それを踏まえて再度ChatGPTに質問してみました。

入力

How can I use variables in vue router?

出力

To use variables in Vue Router, you can define dynamic segments in the route path using a colon before the parameter name. Here's an example:

const router = new VueRouter({
  routes: [
    {
      path: '/users/:userId',
      name: 'user-details',
      component: UserDetail
    }
  ]
})

In this example, :userId is the dynamic segment, and it will match any number or string that appears after the /users/ path segment. When a route with a dynamic segment is matched, the value of the segment is passed to the component as a prop named after the parameter.

To use this parameter in your component, you can define a prop in the component definition like this:

const UserDetail = {
  props: ['userId'],
  template: `
    <div>
      User ID: {{ userId }}
    </div>
  `
}

In this example, the userId prop is defined, and it can be accessed in the component's template using double curly braces ({{ userId }}).

To navigate to a route with a dynamic segment, you can use the $router.push() method, and pass the parameter value as a property on the params object. Here's an example:

<template>
  <li v-for="user in users" :key="user.id">
    <router-link :to="{ name: 'user-details', params: { userId: user.id }}">
      {{ user.name }}
    </router-link>
  </li>
</template>

In this example, the router-link component is used to generate a link to the user-details route. The name property specifies the name of the route, and the params object specifies the dynamic segment value using the userId property. When the link is clicked, the user will be navigated to a URL like localhost:8888/users/1, where 1 is the value of the user.id property.

どうやらroutesを定義する時にnameプロパティも指定する必要があるようです。 上記の回答を参考に、以下のようにrouter.jsのコードを変更してみました。

vue-app/src/router.js

import { createRouter, createWebHistory } from 'vue-router'
import UserList from './components/UserList.vue'
import UserDetails from './components/UserDetails.vue'



const routes = [
  { path: '/users', component: UserList },
  { path: '/users/:id', name: 'user-details', component: UserDetails }
]

const router = createRouter({
  history: createWebHistory(),
  routes: routes
})

export default router

そしてUserList.vueのルーティングは以下のように変更しました。

<li v-for="user in users" :key="user.id">
    <router-link :to="{ name: 'user-details', params: {id: user.id}}">{{ user.name }}
    </router-link>
</li>

すると想定通りルーティングできるようになりました。

あとはユーザー詳細ページからユーザー一覧ページに戻るリンクも欲しいと思ったので、ソースコードにコメントを書くとGitHub Copilotが提案してくれました。

<!-- navigate back to the users list -->
<router-link to="/users">Back to Users</router-link>

上記で想定どおりユーザー一覧画面に戻るリンクが追加できました。 UserDetails.vueの全体のコードは以下のようになりました。

vue-app/src/components/UserDetails.vue

<template>
  <div>
    <h1>User Details</h1>
    <p><strong>Name:</strong> {{ user.name }}</p>
    <p><strong>Email:</strong> {{ user.email }}</p>
    <br>
    <hr>
    <!-- textbox for editing the user -->
    <div>
      <h1>Edit User</h1>
      <label for="name">Name:</label>
      <input type="text" id="name" v-model="editUserName">
      <br><br>
      <label for="email">Email:</label>
      <input type="text" id="email" v-model="editUserEmail">
      <br><br>
    </div>
    <button @click="editUser">Edit</button>
    <button @click="deleteUser">Delete</button>
    <br><br>
    <!-- navigate back to the users list -->
    <router-link to="/users">Back to Users</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      user: {},
      editUserName: '',
      editUserEmail: ''
    }
  },
  mounted() {
    const userId = this.$route.params.id;
    const apiURL = `http://localhost:8000/api/users/${userId}/`;

    fetch(apiURL)
      .then(response => response.json())
      .then(data => {
        this.user = data;
      })
      .catch(error => console.error(error));
  },
  methods: {
    editUser() {
      // send the PUT request to the API and parameters are from Edit User form
      const userId = this.$route.params.id;
      const apiURL = `http://localhost:8000/api/users/${userId}/`;
      fetch(apiURL, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          name: this.editUserName,
          email: this.editUserEmail
        })
      })
      .then(response => response.json())
      .then(data => {
        console.log(data);
        this.user.name = this.editUserName
        this.user.email = this.editUserEmail
      }
      )
      .catch(error => console.error(error))
    },
    deleteUser() {
      const userId = this.$route.params.id;
      const apiURL = `http://localhost:8000/api/users/${userId}/`;

      fetch(apiURL, {
        method: 'DELETE'
      })
      .then(response => response.json())
      .then(data => {
        console.log(data);
        // Navigate back to the user list page
        this.$router.push('/users');
      })
      .catch(error => console.error(error));

    }
  }
}
</script>

ここまでのフロントエンドの実装をGitHubにプッシュしました。

github.com