ぬにょす(挨拶)。
表題の通りですが、PHPフレームワークであるところのLaravelを使ってみようということで、やったこと・できたこと・できなかったこと等を自分の備忘録として残していきます。
#4の今回はBladeテンプレートをVueコンポーネントに変えていきます。
Vue用のBladeテンプレート
Vueインスタンスを割り当てるBladeテンプレートを作成します。スカフォールドが生成した layouts/app.blade.phpをコピーして編集しました。
resources/views/vue.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<script src="{{ asset('js/app.js') }}" defer></script>
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app"></div>
</body>
</html>
Code language: HTML, XML (xml)
<div id=”app”></div>の部分にVueテンプレートが描画されます。
使用テンプレートの変更
Laravelフレームワークが使用するBladeテンプレートを vue.blade.php に変更します。
/
web.php で welcome.blade.php を使用するようになっているので、vue.blade.php を使用するように変更します。ついでに使わなそうな認証関係のルートを無効にしておきます。
routes/web.php
Route::get('/', function (){
return view('vue');
});
Auth::routes([
'reset' => false,
'confirm' => false,
'verify' => false,
]);
Code language: PHP (php)
/login
LoginController が使用している AuthenticatesUsers トレイトに showLoginForm という関数があるので、これをオーバーライドします。
app/Http/Controllers/Auth/LoginController.php
class LoginController extends Controller
{
public function showLoginForm()
{
return view('vue');
}
}
Code language: PHP (php)
/register
RegisterController が使用している RegistersUsers トレイトに showRegistrationForm という関数があるので、これをオーバーライドします。
app/Http/Controllers/Auth/RegisterController.php
class RegisterController extends Controller
{
public function showRegistrationForm()
{
return view('vue');
}
}
Code language: PHP (php)
/home
HomeController の index アクションが home.blade.php を使用するようになっているので、vue.blade.php を使用するように変更します。
app/Http/Controllers/HomeController.php
public function index()
{
return view('vue');
}
Code language: PHP (php)
Vue Routerの導入
アクセスルートに応じたVueコンポーネントを表示するために、Vue Routerを導入します。
npm で vue-router パッケージをインストールします。
npm i -D vue-router
Code language: plaintext (plaintext)
Vue Routerの設定ファイルを作成します。js/app.js にインポートできれば良いので、ファイルの格納場所は js/router.js でも js/router/index.js とかでも構いません。私はなんとなく追加プラグイン関係はまとめておきたいなーと思ったので js/plugins/router.js としました。
resources/js/plugins/router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'welcome',
component: () => import( '../views/WelcomeView.vue'),
},
{
path: '/login',
name: 'login',
component: () => import( '../views/LoginView.vue'),
},
{
path: '/register',
name: 'register',
component: () => import( '../views/RegisterView.vue'),
},
{
path: '/home',
name: 'home',
component: () => import( '../views/HomeView.vue'),
},
]
const router = new VueRouter({
mode: 'history',
routes,
})
export default router
Code language: JavaScript (javascript)
コンポーネントの読み込みは遅延ローディングにしています。
404 | Vue Router
The official Router for Vue.js
app.js にインポートし、Vueインスタンスに注入します。
resources/js/app.js
import router from './plugins/router'
const app = new Vue({
el: '#app',
router,
template: '<router-view />',
});
Code language: JavaScript (javascript)
Laravelフレームワークから返却される vue.blade.php に記述した <div id=”app”></div> の部分が、Vueインスタンスの template として指定した <router-view /> に書き換わり、Vue Router がルート定義に従ってURLに応じたコンポーネントを描画する、という動きになります(という認識です)。
ナビゲーションバーの部品化
ナビゲーションバーは複数の画面で使うので、部品化しておきます。
resources/js/components/NavbarComponent.vue
<template>
<b-navbar toggleable="md" type="light" class="bg-white shadow-sm">
<b-container>
<b-navbar-brand :to="{ name: 'welcome' }">Laravel</b-navbar-brand>
<b-navbar-toggle target="navbarSupportedContent"></b-navbar-toggle>
<b-collapse is-nav id="navbarSupportedContent">
<b-navbar-nav class="mr-auto"></b-navbar-nav>
<b-navbar-nav class="ml-auto">
<b-nav-item :to="{ name: 'login' }">Login</b-nav-item>
<b-nav-item :to="{ name: 'register' }">Register</b-nav-item>
<b-nav-item-dropdown right>
<template v-slot:button-content>{{ user.name }}</template>
<b-dropdown-item @click="logout">Logout</b-dropdown-item>
</b-nav-item-dropdown>
</b-navbar-nav>
</b-collapse>
</b-container>
</b-navbar>
</template>
<script>
export default {
data: function(){
return {
user: {
name: "Username"
}
};
},
methods: {
logout: function(){
console.log("logout");
}
}
};
</script>
<style>
</style>
Code language: HTML, XML (xml)
サーバーとデータをやり取りする部分の実装は、ひとまず置いておきます。
複数のページで使うので、グローバル登録しておきます。
resources/js/app.js
Vue.component(
'navbar-component',
require('./components/NavbarComponent.vue').default
)
Code language: JavaScript (javascript)
ページコンポーネントの作成
各ページに対応する〇〇View.vueは、Laravelのスカフォールドが生成したBladeテンプレートの内容をベースに、一部をBootstrap-Vueコンポーネントに置き換えるなどして <template> へ記述しました。
resources/js/views/WelcomeView.vue
<template>
<div class="flex-center position-ref full-height">
<div class="top-right links">
<b-link :to="{ name: 'home' }">Home</b-link>
<b-link :to="{ name: 'login'}">Login</b-link>
<b-link :to="{ name: 'register' }">Register</b-link>
</div>
<div class="content">
<div class="title m-b-md">Laravel</div>
<div class="links">
<a href="https://laravel.com/docs">Docs</a>
<a href="https://laracasts.com">Laracasts</a>
<a href="https://laravel-news.com">News</a>
<a href="https://blog.laravel.com">Blog</a>
<a href="https://nova.laravel.com">Nova</a>
<a href="https://forge.laravel.com">Forge</a>
<a href="https://vapor.laravel.com">Vapor</a>
<a href="https://github.com/laravel/laravel">GitHub</a>
</div>
</div>
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
Code language: HTML, XML (xml)
Welcome画面の見た目などは今後いじってくことになるので、今は気にしない方向。また、Bladeテンプレートのディレクティブは使えないので、ひとまずゴリッと削っておきます。なのでサーバーからのデータ注入や条件分岐は行われません。
resources/js/views/LoginView.vue
<template>
<div>
<navbar-component />
<main class="py-4">
<b-container>
<b-row class="justify-content-center">
<b-col md="8">
<b-card header="Login">
<b-form @submit.prevent="login">
<b-row class="form-group">
<label for="email" class="col-md-4 col-form-label text-md-right">E-Mail Address</label>
<b-col md="6">
<b-input
v-model="form.email"
:state="errors.email ? false : null"
type="email"
required
autocomplete="email"
autofocus
/>
<b-form-invalid-feedback :state="!(errors.email)">{{ errors.email }}</b-form-invalid-feedback>
</b-col>
</b-row>
<b-row class="form-group">
<label for="password" class="col-md-4 col-form-label text-md-right">Password</label>
<b-col md="6">
<b-input
v-model="form.password"
:state="errors.password ? false : null"
type="password"
required
autocomplete="current-password"
/>
<b-form-invalid-feedback :state="!(errors.password)">{{ errors.password }}</b-form-invalid-feedback>
</b-col>
</b-row>
<b-row class="form-group">
<b-col md="6" offset-md="4">
<b-form-checkbox v-model="form.remember">Remember Me</b-form-checkbox>
</b-col>
</b-row>
<b-row class="form-group mb-0">
<b-col md="8" offset-md="4">
<b-button type="submit" variant="primary">Login</b-button>
</b-col>
</b-row>
</b-form>
</b-card>
</b-col>
</b-row>
</b-container>
</main>
</div>
</template>
<script>
export default {
data: function(){
return {
form: {
email: "",
password: "",
remember: false
},
errors: {
email: "",
password: ""
}
};
},
methods: {
login: function(){
console.log("login");
}
}
};
</script>
<style>
</style>
Code language: HTML, XML (xml)
こちらも data や methods はプレースホルダー的な状態です。
resources/js/views/RegisterView.vue
<template>
<div>
<navbar-component />
<main class="py-4">
<b-container>
<b-row class="justify-content-center">
<b-col md="8">
<b-card header="Register">
<b-form @submit.prevent="register">
<b-row class="form-group">
<label for="name" class="col-md-4 col-form-label text-md-right">Name</label>
<b-col md="6">
<b-input
v-model="form.name"
:state="errors.name ? false : null"
type="text"
required
autocomplete="name"
autofocus
/>
<b-form-invalid-feedback :state="!(errors.name)">{{ errors.name }}</b-form-invalid-feedback>
</b-col>
</b-row>
<b-row class="form-group">
<label for="email" class="col-md-4 col-form-label text-md-right">E-Mail Address</label>
<b-col md="6">
<b-input
v-model="form.email"
:state="errors.email ? false : null"
type="email"
required
autocomplete="email"
/>
<b-form-invalid-feedback :state="!(errors.email)">{{ errors.email }}</b-form-invalid-feedback>
</b-col>
</b-row>
<b-row class="form-group">
<label for="password" class="col-md-4 col-form-label text-md-right">Password</label>
<b-col md="6">
<b-input
v-model="form.password"
:state="errors.password ? false : null"
type="password"
required
autocomplete="new-password"
/>
<b-form-invalid-feedback :state="!(errors.password)">{{ errors.password }}</b-form-invalid-feedback>
</b-col>
</b-row>
<b-row class="form-group">
<label
for="password-confirm"
class="col-md-4 col-form-label text-md-right"
>Confirm Password</label>
<b-col md="6">
<b-input
v-model="form.password_confirmation"
type="password"
required
autocomplete="new-password"
/>
</b-col>
</b-row>
<b-row class="form-group mb-0">
<b-col md="8" offset-md="4">
<b-button type="submit" variant="primary">Register</b-button>
</b-col>
</b-row>
</b-form>
</b-card>
</b-col>
</b-row>
</b-container>
</main>
</div>
</template>
<script>
export default {
data: function(){
return {
form: {
email: "",
password: "",
password_confirmation: ""
},
errors: {
name: "",
email: "",
password: ""
}
};
},
methods: {
register: function(){
console.log("register");
}
}
};
</script>
<style>
</style>
Code language: HTML, XML (xml)
resources/js/views/HomeView.vue
<template>
<div>
<navbar-component />
<main class="py-4">
<b-container>
<b-row class="justify-content-center">
<b-col md="8">
<b-card header="Dashboard">
<b-alert variant="success" show>You are logged in!</b-alert>
</b-card>
</b-col>
</b-row>
</b-container>
</main>
</div>
</template>
<script>
export default {
data: function(){
return {};
},
methods: {}
};
</script>
<style>
</style>
Code language: HTML, XML (xml)
動作確認
Vue DevTools でコンポーネントが描画されていることを確認できました。
次回は
axiosでの認証処理を実装します。
コメント