はじめに
Vue.jsとLaravelによるSPA実装のチュートリアル記事です。
本記事は、4本の連載記事の4本目です。
Vue.js + LaravelでシンプルなSPA構築チュートリアル:概要編
Vue.js + LaravelでシンプルなSPA構築チュートリアル:Vueフロントエンド編
Vue.js + LaravelでシンプルなSPA構築チュートリアル:LaravelAPI編
Vue.js + LaravelでシンプルなSPA構築チュートリアル:VueとAPI結合編
↑↑今ここ↑↑
前回まで
Vue.jsでフロントエンド実装と、
LaravelのAPI実装が完了しました。
APIにつないでない状態の
・タスク一覧
・タスク詳細
・タスク登録
・タスク編集
のページと、
・タスク一覧取得API
・タスク詳細取得API
・タスク登録API
・タスク更新API
・タスク削除API
が完成している状態です。
今回はこの静的ページと
APIを繋ぎ込んでいきます。
この全体図の赤色部分になります。
axios
今回、フロントページから
AjaxでAPIにリクエストを送信して
データの取得や更新を行います。
Ajax通信を簡単に実装するため、
今回はaxiosというパッケージを利用します。
https://qiita.com/ksh-fthr/items/2daaaf3a15c4c11956e9
特に難しいところはありませんが、
axiosの使い方を簡単に把握しておきましょう。
laravel/uiでベースを構築したので、
自分でインストールや設定作業などしなくても
最初からaxiosが利用できる状態です。
タスク一覧取得API繋ぎ込み
早速、タスク一覧ページとタスク一覧取得APIを繋ぎ込んでみましょう。
まずは<script>
に必要なデータ、メソッドを定義します。
<script>
- export default {}
+ export default {
+ data: function () {
+ return {
+ tasks: []
+ }
+ },
+ methods: {
+ getTasks() {
+ axios.get('/api/tasks')
+ .then((res) => {
+ this.tasks = res.data;
+ });
+ }
+ },
+ mounted() {
+ this.getTasks();
+ }
+ }
</script>
まず data
には空配列の tasks
を用意します。
そして、 methods
にある getTasks()
メソッドで、
タスク一覧取得APIにリクエストして
そのレスポンスを先ほどの tasks
の中に入れています。
(このメソッドで先ほど話したaxiosを利用してリクエストしています)
そして、画面描画時にこの getTasks()
メソッドが実行されるように、
mounted()
でメソッドを呼び出しています。
これで<script>
側は完了です。
次に<templete>
側も修正します。
- <tr>
- <th scope="row">1</th>
- <td>Title1</td>
- <td>Content1</td>
- <td>Ichiro</td>
- <td>
- <button class="btn btn-primary">Show</button>
- </td>
- <td>
- <button class="btn btn-success">Edit</button>
- </td>
- <td>
- <button class="btn btn-danger">Delete</button>
- </td>
- </tr>
- <tr>
- <th scope="row">2</th>
- <td>Title2</td>
- <td>Content2</td>
- <td>Jiro</td>
- <td>
- <button class="btn btn-primary">Show</button>
- </td>
- <td>
- <button class="btn btn-success">Edit</button>
- </td>
- <td>
- <button class="btn btn-danger">Delete</button>
- </td>
- </tr>
- <tr>
- <th scope="row">3</th>
- <td>Title3</td>
- <td>Content3</td>
- <td>Saburo</td>
- <td>
- <button class="btn btn-primary">Show</button>
- </td>
- <td>
- <button class="btn btn-success">Edit</button>
- </td>
- <td>
- <button class="btn btn-danger">Delete</button>
- </td>
- </tr>
+ <tr v-for="(task, index) in tasks" :key="index">
+ <th scope="row">{{ task.id }}</th>
+ <td>{{ task.title }}</td>
+ <td>{{ task.content }}</td>
+ <td>{{ task.person_in_charge }}</td>
+ <td>
+ <router-link v-bind:to="{name: 'task.show', params: {taskId: task.id }}">
+ <button class="btn btn-primary">Show</button>
+ </router-link>
+ </td>
+ <td>
+ <router-link v-bind:to="{name: 'task.edit', params: {taskId: task.id }}">
+ <button class="btn btn-success">Edit</button>
+ </router-link>
+ </td>
+ <td>
+ <button class="btn btn-danger">Delete</button>
+ </td>
+ </tr>
まずはべた書きで表示していた
3行のデータを削除します。
そして、先ほど定義したtasks
データをv-for
で表示します。
<tr v-for="(task, index) in tasks" :key="index">
ID、Title、Content、Person In Chargeの
各カラムは {{ task.title }}
のようにデータを動的に表示させます。
- <td>Title1</td>
+ <td>{{ task.title }}</td>
また、「Show」「Edit」ボタンの
リンクURLのパラメータもべた書きしていたので、
ちゃんと動的にidを設定します。
- <router-link v-bind:to="{name: 'task.show', params: {taskId: 3}}">
+ <router-link v-bind:to="{name: 'task.show', params: {taskId: task.id }}">
これで、
APIからデータを取得し
それをv-for
で画面に一覧表示できるようになりました。
commit:タスク一覧ページAPI繋ぎ込み
タスク一覧ページ完成です。
タスク詳細取得API繋ぎ込み
次に、タスク詳細ページとタスク詳細取得APIを繋ぎ込んでいきます。
まずは<script>
。
<script>
export default {
props: {
taskId: String
},
+ data: function () {
+ return {
+ task: {}
+ }
+ },
+ methods: {
+ getTask() {
+ axios.get('/api/tasks/' + this.taskId)
+ .then((res) => {
+ this.task = res.data;
+ });
+ }
+ },
+ mounted() {
+ this.getTask();
+ }
}
</script>
※taskId: StringではエラーになるのでNumberにした、というコメントもありますので、もしうまく動かなければ参考にしてください
一覧ページと同じように、
data
に空のtask
を用意。
methods
の getTask()
でAPIからタスクデータを取得。
mounted()
で画面描画時にメソッド呼び出し。
としています。
次に<templete>
側。
<div class="form-group row border-bottom">
<label for="id" class="col-sm-3 col-form-label">ID</label>
<input type="text" class="col-sm-9 form-control-plaintext" readonly id="id"
- v-bind:value="taskId">
+ v-model="task.id">
</div>
<div class="form-group row border-bottom">
<label for="title" class="col-sm-3 col-form-label">Title</label>
<input type="text" class="col-sm-9 form-control-plaintext" readonly id="title"
- value="title title">
+ v-model="task.title">
</div>
<div class="form-group row border-bottom">
<label for="content" class="col-sm-3 col-form-label">Content</label>
<input type="text" class="col-sm-9 form-control-plaintext" readonly id="content"
- value="content content">
+ v-model="task.content">
</div>
<div class="form-group row border-bottom">
<label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label>
<input type="text" class="col-sm-9 form-control-plaintext" readonly id="person-in-charge"
- value="Ichiro">
+ v-model="task.person_in_charge">
</div>
各データをv-model
で表示するようにしました。
これでAPI取得したデータをタスク詳細ページに表示できました。
commit:タスク詳細ページAPI繋ぎ込み
タスク詳細ページ完成です。
タスク登録API繋ぎ込み
次に、タスク登録ページとタスク登録APIを繋ぎ込んでいきます。
まずは<script>
。
<script>
- export default {}
+ export default {
+ data: function () {
+ return {
+ task: {}
+ }
+ },
+ methods: {
+ submit() {
+ axios.post('/api/tasks', this.task)
+ .then((res) => {
+ this.$router.push({name: 'task.list'});
+ });
+ }
+ }
+ }
</script>
空のtask
データを用意するところは先ほどと同じです。
methods
の submit()
メソッドで、
task
データをタスク登録APIにPOST送信する処理を書いています。
また、APIによるデータ登録完了後、
this.$router.push({name: 'task.list'});
でタスク一覧ページにリダイレクトしています。
次に<templete>
側。
- <form>
+ <form v-on:submit.prevent="submit">
<div class="form-group row">
<label for="title" class="col-sm-3 col-form-label">Title</label>
- <input type="text" class="col-sm-9 form-control" id="title">
+ <input type="text" class="col-sm-9 form-control" id="title" v-model="task.title">
</div>
<div class="form-group row">
<label for="content" class="col-sm-3 col-form-label">Content</label>
- <input type="text" class="col-sm-9 form-control" id="content">
+ <input type="text" class="col-sm-9 form-control" id="content" v-model="task.content">
</div>
<div class="form-group row">
<label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label>
- <input type="text" class="col-sm-9 form-control" id="person-in-charge">
+ <input type="text" class="col-sm-9 form-control" id="person-in-charge" v-model="task.person_in_charge">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
各フォームはv-model
でtask
データとバインディングすることで、
フォームにデータが入力されたら
<scripts>
側のtask
データも更新されるようになっています。
そして、
<form v-on:submit.prevent="submit">
で、フォーム送信時に先ほど定義したsubmit
メソッドを呼び出すようにしています。
これで、入力内容が反映されたtask
データを
submit
メソッドでAPI送信できる状態になっています。
commit:タスク登録ページAPI繋ぎ込み
これでタスク登録ページ完成です。
タスク更新API繋ぎ込み
次に、タスク編集ページとタスク更新APIを繋ぎ込んでいきます。
まずは<script>
。
<script>
export default {
props: {
taskId: String
},
+ data: function () {
+ return {
+ task: {}
+ }
+ },
+ methods: {
+ getTask() {
+ axios.get('/api/tasks/' + this.taskId)
+ .then((res) => {
+ this.task = res.data;
+ });
+ },
+ submit() {
+ axios.put('/api/tasks/' + this.taskId, this.task)
+ .then((res) => {
+ this.$router.push({name: 'task.list'})
+ });
+ }
+ },
+ mounted() {
+ this.getTask();
+ }
}
</script>
タスク詳細ページとタスク登録ページでやったことを
両方やっているだけです。
空のtask
データを用意し、
getTask()
メソッドでAPIから取得したデータをセットする。
submit
メソッドでは、
タスク更新APIにputリクエストを送信しています。
次に<template>
。
- <form>
+ <form v-on:submit.prevent="submit">
<div class="form-group row">
<label for="id" class="col-sm-3 col-form-label">ID</label>
- <input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-bind:value="taskId">
+ <input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-model="task.id">
</div>
<div class="form-group row">
<label for="title" class="col-sm-3 col-form-label">Title</label>
- <input type="text" class="col-sm-9 form-control" id="title">
+ <input type="text" class="col-sm-9 form-control" id="title" v-model="task.title">
</div>
<div class="form-group row">
<label for="content" class="col-sm-3 col-form-label">Content</label>
- <input type="text" class="col-sm-9 form-control" id="content">
+ <input type="text" class="col-sm-9 form-control" id="content" v-model="task.content">
</div>
<div class="form-group row">
<label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label>
- <input type="text" class="col-sm-9 form-control" id="person-in-charge">
+ <input type="text" class="col-sm-9 form-control" id="person-in-charge" v-model="task.person_in_charge">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
これはタスク登録ページと同じです。
各フォームはv-model
でtask
データとバインディングして、
formの v-on:submit.prevent="submit"
でsumit
メソッドを呼んでいます。
commit:タスク編集ページAPI繋ぎ込み
これでタスク編集ページは完成。
タスク削除API繋ぎ込み
最後に、タスク一覧ページのDeleteボタンとタスク削除APIを繋ぎ込んでいきます。
まずは<script>
。
methods: {
getTasks() {
axios.get('/api/tasks')
.then((res) => {
this.tasks = res.data;
});
},
+ deleteTask(id) {
+ axios.delete('/api/tasks/' + id)
+ .then((res) => {
+ this.getTasks();
+ });
+ }
},
deleteTask()
メソッドを追加しました。
タスクIDを引数で受け取り、
タスク削除APIにリクエストを送信しています。
削除完了したら、
getTasks()
メソッドを呼んで
タスク一覧を再読み込みしています。
次に<template>
。
<td>
- <button class="btn btn-danger">Delete</button>
+ <button class="btn btn-danger" v-on:click="deleteTask(task.id)">Delete</button>
</td>
もともと設置していたDeleteボタンに
v-on:click="deleteTask(task.id)"
を追加しました。
これで、このボタンをクリックしたら deleteTask()
メソッドが呼ばれます。
commit:タスク一覧ページ削除API繋ぎ込み
これでタスク一覧ページの削除処理もできたので、
全ページ、全機能が完成しました。
おわりに
シンプルなCRUD機能のアプリを
Vue.jsのSPAとLaravelのAPIで構築しました。
Vue側もLaravel側もほとんど難しいところもなく、
かなり簡単に書けたと思います。
今回はできるだけ簡単に一通りの機能を作るチュートリアルとしたかったため、
本来実装すべき処理を省いた箇所が多いです。
Vue側では
Ajaxのエラーハンドリングや
API送信前のバリデーションなど
本来は実装すべきです。
Laravel側もバリデーションや
APIの認証処理などがあるといいです。
今回のチュートリアルで
ざっくりと全体イメージをまずはつかんで、
今後上記のような詳細な処理を少しずつ追加していくといいかと思います。
(最後まで見ていただいた方、最初の記事からLGTMしてもらえるとうれしいです)