ユーザを扱うAPIが一通りできたので、Vue.jsを使ってユーザ管理UIを作っていきます。
まずはVue.jsをインストール
laravelにはpackage.jsonに最初からVue.jsが含まれているのでそのままnpm installします。
$ npm install
追加で、SPA的なルーティングのためにvue-routerも入れておきます。
$ npm install vue-router --save-dev
ベースとなるHTMLをlaravel側で出力する
laravel側のルーティングですべてのアクセスに対してひとつのviewを返すように設定します。
/*Route::get('/', function () { return view('welcome'); });*/ Route::get('/{catchall}', function () { return view('app'); })->where('catchall', '(.*)');
<!DOCTYPE html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>Laravel5.5でREST API + SPA</title> <link rel="stylesheet" href="{{ mix('css/app.css') }}"> </head> <body> <div id="app"> <div class="container"> <router-view></router-view> </div> </div> </body> <script src="{{ mix('js/app.js') }}"></script> </html>
npm run watch
vue側の自動ビルドも起動しておきます。
http://127.0.0.1:8000/にアクセスすると上記HTMLが出力されるはずです。(見た目は真っ白ですが)
画面構成を決めてVue.js側でルーティング
laravel側ルーティングは共通HTMLを返すだけなので、実質のルーティングはvue-routerで行います。
/** * First we will load all of this project's JavaScript dependencies which * includes Vue and other libraries. It is a great starting point when * building robust, powerful web applications using Vue and Laravel. */ require('./bootstrap'); window.Vue = require('vue'); import VueRouter from 'vue-router'; Vue.use(VueRouter); /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application * or customize the JavaScript scaffolding to fit your unique needs. */ //Vue.component('example-component', require('./components/ExampleComponent.vue')); const router = new VueRouter({ mode: 'history', routes: [ { path: '/users', component: require('./components/Users.vue'), children: [ {path: '/users/', component: require('./components/Users/Index.vue')}, {path: '/users/edit', component: require('./components/Users/Edit.vue')}, {path: '/users/edit/:id', component: require('./components/Users/Edit.vue')}, ] }, ] }) const app = new Vue({ router, el: '#app' });
/users/ でユーザ一覧を表示し、そこからリンクで各ユーザの編集画面に飛ぶことを想定したルーティングです。
APIに接続するロジックをサービスとして作成
import axios from 'axios' /** * Responsible for all HTTP requests. */ export default { request (method, url, data, successCb = null, errorCb = null) { axios.request({ url, data, method: method.toLowerCase() }).then(successCb).catch(errorCb) }, get (url, successCb = null, errorCb = null) { return this.request('get', url, {}, successCb, errorCb) }, post (url, data, successCb = null, errorCb = null) { return this.request('post', url, data, successCb, errorCb) }, put (url, data, successCb = null, errorCb = null) { return this.request('put', url, data, successCb, errorCb) }, delete (url, data = {}, successCb = null, errorCb = null) { return this.request('delete', url, data, successCb, errorCb) }, /** * Init the service. */ init () { axios.defaults.baseURL = '/api' // Intercept the request to make sure the token is injected into the header. axios.interceptors.request.use(config => { config.headers['Authorization'] = `Bearer ${localStorage.getItem('jwt-token')}`; return config }) // Intercept the response and ... axios.interceptors.response.use(response => { // ...get the token from the header or response data if exists, and save it. const token = response.headers['Authorization'] || response.data['token']; if (token) { localStorage.setItem('jwt-token', token) } return response }, error => { // Also, if we receive a Bad Request / Unauthorized error console.log(error) return Promise.reject(error) }) } }
httpサービスはapp.jsで初期化します。
const app = new Vue({ created () { http.init(); }, router, el: '#app' });
各画面のvueコンポーネント作成
users/以下のベースになるHTML
<template> <div> <p>this is Users.vue</p> <router-view></router-view> </div> </template>
このrouter-viewの中に以下のテンプレートがインクルードされます。
ユーザ一覧画面
<template> <div> <p>this is Users/Index.vue</p> <router-link tag="button" v-bind:to="{ path : '/users/edit/'}">新規登録</router-link> <ul v-for="user in users"> <li> {{user.name}} </li> <li> {{user.email}} </li> <router-link tag="button" v-bind:to="{ path : '/users/edit/' + user.id}">編集</router-link> <button v-on:click="removeUser(user)">削除</button> </ul> <div class="form-group"> </div> </div> </template> <script> import http from '../../services/http' export default { mounted() { this.fetchUsers() }, data() { return { users: [], name: '', showAlert: false, alertMessage: '', } }, methods: { fetchUsers() { http.get('users', res => { this.users = res.data; }) }, removeUser(user) { http.delete('users/' + user.id, {}, () => { alert(user.name + 'を削除しました'); this.fetchUsers(); }) }, } } </script>
ユーザ編集(追加)画面
<template> <div> <p>this is Users/Edit.vue</p> <input type="text" v-model="name" placeholder="name"> <input type="text" v-model="email" placeholder="email"> <input type="password" v-model="password"> <button class="btn btn-primary" @click='submit'>登録</button> </div> </template> <script> import http from '../../services/http' export default { mounted() { this.userId = this.$route.params.id; if (this.userId) { this.fetchUser(this.userId) } }, data() { return { userId: null, name: '', email: '', password: '' } }, methods: { fetchUser(id) { http.get('users/' + id, res => { this.name = res.data.name; this.email = res.data.email; }) }, submit() { let user = { name: this.name, email: this.email, }; if (this.password) { user['password'] = this.password } if (this.userId) { http.put('users/' + this.userId, user, (res) => { console.log(res); alert(user.name + 'を編集しました'); this.$router.push('/users/'); }) } else { http.post('users', user, (res) => { console.log(res); alert(user.name + 'を新規登録しました'); this.$router.push('/users/'); }) } }, } } </script>