laravelでAPI作って、フロントはvue.js+axios(非同期なajax)で、SPAなCRUDアプリを作ってみた(前編)
参考URL:
https://qiita.com/minato-naka/items/2d2def4d66ec88dc3ca2
1, laravel本体をインストール。laravel/uiでvueをインストール
2, SPAのために、vue-routerをインストール
3, どんなURLでも、同じページ(app.blade.php)に行くようにルーティングを指定する。
4, コンポーネント(vueの画面パーツ)は、resources/js/components/フォルダにガンガン作っていく
共通ヘッダ・一覧・詳細・更新・新規作成の画面(ハリボテ状態)
5, DB設定はMySQLではなくSQLite
6, laravelでCRUDなREST APIを作る
7, ハリボテだった画面コンポーネントからAPIをaxios(ajax)経由でコールする。
作ってみた感想としては、削除は画面遷移しないのでスムーズだけど、それ以外は対して変わらない(一覧表示とか遅い)
リアルタイムチャットみたいにサーバプッシュな処理がないと、有り難みないな~。
1, laravel本体をインストール。laravel/uiでvueをインストール
2, SPAのために、vue-routerをインストール
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# laravel本体をインストール composer create-project --prefer-dist laravel/laravel vue-laravel-spa cd vue-laravel-spa # uiというフロントエンド開発用のツールをインストール composer require laravel/ui # uiを使って、bootstrap、jquery、vueをpackage.jsonに追記 php artisan ui vue # 実際にnodeパッケージをインストールする。node_modulesに格納 npm install # SPA用にVue Routerをインストール npm install --save vue-router # フロントエンドをビルドする npm run dev |
/public/js/app.jsと/public/css/app.cssが生成される。
git管理外にするために、.gitignoreに記述しておく
/public/js
/public/css
この時点では下準備(パッケージのインストール)が整っただけで、ブラウザの見た目は何も変わっていない。
php artisan serve
ここからVue-RouterでSPAを作る。
3, どんなURLでも、同じページ(app.blade.php)に行くようにルーティングを指定する。
最初からあるExample Componentを表示する。
1 2 3 4 5 6 |
// Route::get('/', function () { // return view('welcome'); // }); Route::get('/{any}', function() { return view('app'); })->where('any', '.*'); |
SPAとなる、resources/view/app.blade.phpを作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Vue Laravel SPA') }}</title> <!-- Styles --> <link href="{{ mix('/css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <example-component></example-component> </div> <!-- Scripts --> <script src="{{ mix('/js/app.js') }}" defer></script> </body> </html> |
http://127.0.0.1:8000/abc など適当なURLを入力しても、404エラーにならず、全て同じ表示になる。
4, コンポーネント(vueの画面パーツ)は、resources/js/components/フォルダにガンガン作っていく
ExampleComponent.vueだけ最初からある。
共有ヘッダ(HeaderComponent.vue)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<template> <div class="container-fluid bg-dark mb-3"> <div class="container"> <nav class="navbar navbar-dark"> <span class="navbar-brand mb-0 h1">Vue Laravel SPA</span> <div> <button class="btn btn-success">List</button> <button class="btn btn-success">ADD</button> </div> </nav> </div> </div> </template> <script> export default {} </script> |
ブラウザ画面に表示させるには
resources/js/app.jsにimportする
1 2 3 4 5 6 7 8 9 10 11 12 |
+ import HeaderComponent from "./components/HeaderComponent"; //↑ファイル先頭 Vue.component('example-component', require('./components/ExampleComponent.vue').default); + Vue.component('header-component', HeaderComponent); コンポーネントを差し替え resources/views/app.blade.php <div id="app"> - <example-component></example-component> + <header-component></header-component> </div> |
npm run devするか、npm run watchしっぱなしで、リビルドするとブラウザ表示が変わってヘッダ・コンポーネントが表示される。
メイン部分のコンポーネント作成
resources/js/components/TaskListComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<template> <div class="container"> <table class="table table-hover"> <thead class="thead-light"> <tr> <th scope="col">#</th> <th scope="col">Title</th> <th scope="col">Content</th> <th scope="col">Person In Charge</th> <th scope="col">Show</th> <th scope="col">Edit</th> <th scope="col">Delete</th> </tr> </thead> <tbody> <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> </tbody> </table> </div> </template> <script> export default {} </script> |
作成したメイン部分のコンポーネントとvue-routerをインポートする
resources/js/app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// SPA用ルータ import VueRouter from 'vue-router'; import HeaderComponent from "./components/HeaderComponent"; // メイン部分のコンポーネント import TaskListComponent from "./components/TaskListComponent"; window.Vue = require('vue'); // Vueルータの定義 Vue.use(VueRouter); const router = new VueRouter({ mode: 'history', routes: [ { path: '/tasks', name: 'task.list', component: TaskListComponent }, ] }); const app = new Vue({ el: '#app', router // これを忘れずに! }); |
app.blade.phpで、vue-routerの表示部分となるを追記(ここがURLパスで切り替わる)
1 2 3 4 |
<div id="app"> <header-component></header-component> <router-view></router-view> </div> |
これで /tasks だけはタスク一覧が表示されるようになった。
http://localhost:8000/tasks
ヘッダボタンのリンクを作っておく
resources/js/components/HeaderComponent.vue
1 2 3 4 5 6 |
<div> <router-link v-bind:to="{name: 'task.list'}"> <button class="btn btn-success">List</button> </router-link> <button class="btn btn-success">ADD</button> </div> |
タスクの詳細ページのコンポーネントを作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
resources/js/components/TaskShowComponent.vue <template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form> <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"> </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"> </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"> </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"> </div> </form> </div> </div> </div> </template> <script> export default { props: { taskId: String } } </script> |
Vue-Routerに追記
resources/js/app.js
1 2 3 4 5 6 7 8 9 |
// タスク詳細画面 import TaskShowComponent from "./components/TaskShowComponent"; { path: '/tasks/:taskId', name: 'task.show', component: TaskShowComponent, props: true }, |
詳細画面がブラウザ表示されるようになる。
http://localhost:8000/tasks/3
タスク一覧画面の詳細ボタンのリンクを修正
resources/js/components/TaskListComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<router-link v-bind:to="{name: 'task.show', params: {taskId: 1}}"> <button class="btn btn-primary">Show</button> </router-link> <router-link v-bind:to="{name: 'task.show', params: {taskId: 2}}"> <button class="btn btn-primary">Show</button> </router-link> <router-link v-bind:to="{name: 'task.show', params: {taskId: 3}}"> <button class="btn btn-primary">Show</button> </router-link> |
登録・修正のWebフォームだけ作って、中身の処理は無し!
タスク登録コンポーネント
resources/js/components/TaskCreateComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form> <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"> </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"> </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"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> </template> <script> export default {} </script> |
app.jsにルーティング追加
1 2 3 4 5 |
{ path: '/tasks/create', name: 'task.create', component: TaskCreateComponent }, |
共通ヘッダに作成画面へのリンクボタン
1 2 3 |
<router-link v-bind:to="{name: 'task.create'}"> <button class="btn btn-success">ADD</button> </router-link> |
タスク編集画面も作っておく
resources/js/components/TaskEditComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form> <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"> </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"> </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"> </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"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> </template> <script> export default { props: { taskId: String } } </script> |
app.jsにルーティング追加
1 2 3 4 5 6 |
{ path: '/tasks/:taskId/edit', name: 'task.edit', component: TaskEditComponent, props: true }, |
ボタン表示部分も修正。
1 2 3 |
<router-link v-bind:to="{name: 'task.edit', params: {taskId: 3}}"> <button class="btn btn-success">Edit</button> </router-link> |
長くなってきたので、後編へ~。