[Laravel5.5でREST API + SPA] Vue.jsでUI作成

ユーザを扱う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>

 

 

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連する投稿

検索語を上に入力し、 Enter キーを押して検索します。キャンセルするには ESC を押してください。

トップに戻る