この記事は、静岡 Advent Calendar 2019 の25日目の記事です。
はじめに
静岡の勉強会事情等々、皆様がいろいろと書いてくださっているので、自分はそちらの内容はまたの機会にすることにして、当初、Laravel を用いてテストについて何か書こうかなと思っておりました。
が、ちょっとばかり路線変更しまして、Vue.js の フォーム を中心に時間が許す限り何か思うがままに書こうかなと思います。
テストにつきましては、また別の機会にでも書きます。sqlite を使ってやるよくあるやつです。大した内容ではございません。
それから、次点として当初考えておりました、Laravel で過去に作った簡単なプロジェクトを Symfony(完全未経験) で書き直す。というものも考えてはおりましたが、風邪で昨日まで1週間寝込むという愚行を犯した自分には難しかったです。(今のところ娘に移っていないようなのが救いだったり...)
うだうだと書きましたが、それでは、早速本題に入りますよ。
今回は下記スクリーンショットのような form を想定しております。
環境につきましては、バックエンドに Laravel (今回あまり関係ございませんが、ディレクトリ名とか Vue CLI のものとは異なります)を、フロントエンドに Vue.js / tailwindcss (Laravel Mixを使用)を使用いたします。
tailwindcss?? なんじゃそりゃ? という方は、こちらをご参照くださいませ。
CSSのクラスとして提供しているだけであり、個人的には、UI系のフレームワークより使い勝手がいいかなと思っています。作者さんはAdam Wathanです。
まぁ、Laravel / Vue 関連クラスターの著名な方なので、なんとなく安心感もありますね笑
何はともあれ、まずはセットアップから〜
Vue.js と tailwindcss のセットアップ
それでは、最初に Vue.js と tailwindcss の下準備とセットアップでもしましょうか。
どこか好きな ディレクトリ に新規で Laravel プロジェクト を作りましょう。
$ composer create-project laravel/laravel プロジェクト名 "バージョン指定.*"
Application key XXXXXXXXXうにゃうにゃでます set successfully.
set successfully. と出ていたらインストールが完了です。インストールが終わりましたら、今作成したプロジェクトのディレクトリへ移動しておきましょう。
また、エディタでもプロジェクトを開いておきます。
下記のコマンドを打って下準備をはじめましょう。
$ php artisan preset none
これで package.json の構成が最小限のものに変わったかと思います。
続きまして、今日の本題 Vue.js を入れていきましょう。
$ php artisan preset vue
これで、package.json でも "vue": "^2.5.7",
な感じに追加されていることが確認できるかと思います。
npm or yarn で Vue Router と tailwindcss を入れる
## Using npm
npm install vue-router tailwindcss --save-dev
## Using Yarn
yarn add vue-router tailwindcss --save-dev
package.json に、下記のものが追加されていればOKです。
"devDependencies": {
"tailwindcss": "^1.1.4",
"vue": "^2.5.7",
"vue-router": "^3.1.3"
},
あともう少しです。
CSS に tailwindcss を追加します
resources/assets/sass/app.scss に下記の内容を書きます。
@tailwind base;
@tailwind components;
@tailwind utilities;
tailwindcssは、ビルド時にこれらのディレクティブを、生成されたすべてのCSSと交換します。
tailwindcss 構成ファイルを作成します
下記のコマンドを打ち込み、tailwind.config.jsを作成します。
npx tailwind init
これにより、tailwind.config.js が、プロジェクトのルートディレクトリの直下に作成され、最小限のファイルが作成されます。tailwindcssに何か追加したいときに追加していくものですね。
詳細はこちら。
webpack.mix.js を修正する (Laravel5.5 の場合)
let mix = require('laravel-mix');
let tailwindcss = require('tailwindcss')
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/assets/js/app.js', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css')
.options({
processCssUrls: false,
postCss: [ tailwindcss('./tailwind.config.js') ],
});
webpack.mix.js を修正する。(Laravel6.0 の場合)
const mix = require('laravel-mix');
const tailwindcss = require('tailwindcss')
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.options({
processCssUrls: false,
postCss: [ tailwindcss('./tailwind.config.js') ],
});
npm or yarn で ビルド する
## Using npm
npm run watch
## Using yarn
yarn run watch
ちょっと長かったですね、あとちょっとです。
resources/views/layouts/app.blade.php の bladeテンプレートの、<div id="app"></div>
の中を、下記のように変更します。
<!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">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<div class="h-screen">
@yield('content')
</div>
</div>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
それから、resources/views/home.blade.php も、下記のように変更しましょう。
resources/assets/js/components/App.vue を読み込むように記載しています。
@extends('layouts.app')
@section('content')
<App></App>
@endsection
続きまして、resources/assets/js/components/App.vue も下記のように書きます。
ポイントは、<router-link to="/contacts/create">
の箇所です。
/contacts/create のURLに遷移するように設定します。 ( Vue Router用の設定 )
<template>
<div>
<router-link to="/contacts/create">
<p>新規登録</p>
</router-link>
</div>
</template>
ここまで出来たので、冒頭に導入した Vue Router を使ってみます。
resources/assets/js/app.js と resources/assets/js/router.js を、それぞれ開きます。
まずは、resources/assets/js/app.js から修正していきますね。
下記のようにしておきましょう。
import Vue from 'vue';
import router from "./router";
import App from "./components/App";
const app = new Vue({
el: '#app',
components: {
App
},
router
});
続いて、resources/assets/js/router.js では、ヒストリーモードを指定し、URLが、/contacts/create のときに、ContactsCreate のコンポーネントが表示されるようにします。
import Vue from 'vue';
import VueRouter from 'vue-router';
import ContactsCreate from "./views/ContactsCreate";
Vue.use(VueRouter);
export default new VueRouter({
routes: [
{ path: '/contacts/create', name: 'ContactsCreate', component: ContactsCreate }
],
mode: 'history'
});
それでは、下準備が終わったところで、
まずは、ファイルを2つ用意しましょう。それぞれ、views/ContactsCreate.vue と、components/InputField.vue を作ります。
views/ContactsCreate.vue は、先ほど resources/assets/js/router.js で 指定したコンポーネントのファイルになりますね。
ContactsCreate.vue から作業を進めていきます。
一旦、props を用いまして、親コンポーネントであります ContactsCreate.vue から、
子コンポーネントの components/InputField.vue へ、
v-bind を 使って、label や placeholder の値を受け渡すところまで書いてみます。
<template>
<div>
<form>
<input-field name="name" label="お名前" placeholder="例:山田ルイ53世"/>
<input-field name="email" label="メールアドレス" placeholder="例:test@test.jp"/>
<input-field name="company" label="組織名" placeholder="例:藤原カンパニー" />
<input-field name="birthday" label="生年月日" placeholder="例:MM/DD/YYYY"/>
<div class="flex justify-end relative pb-4">
<button class="py-2 px-4 text-blue-500 border border-blue-500 mr-5 hover:bg-gray-100 hover:border-blue-300 rounded">キャンセル</button>
<button class="bg-blue-500 border border-blue-500 py-2 px-4 text-white hover:bg-blue-400 mr-1 rounded">新規登録</button>
</div>
</form>
</div>
</template>
<script>
import InputField from "../components/InputField";
export default {
name: "ContactsCreate",
components: {
InputField,
},
data() {
return {
form: {
'name': '',
'email': '',
'company': '',
'birthday': ''
}
}
}
}
</script>
<style scoped>
</style>
続きまして、components/InputField.vue です。
props に、'name', 'label', 'placeholder' を指定しております。
<template>
<div class="relative pb-4">
<label :for="name" class="text-blue-500 pt-2 uppercase text-xs font-bold absolute">{{ label }}</label>
<input :id="name" type="text" class="pt-8 w-full text-gray-900 border-b pb-2 focus:outline-none focus:border-blue-400" :placeholder="placeholder">
</div>
</template>
<script>
export default {
name: "InputField",
props: [
'name', 'label', 'placeholder'
],
data() {
return {
value: ''
}
}
}
</script>
<style scoped>
</style>
tailwindcss の部分も書いておりますので、ちょっとごちゃごちゃと見難い部分があるかと思いますが、
これで、name や placeholder、label に値が受け渡されたのが確認できるかと思います。
次は、今やったこととは反対に、子コンポーネントから親コンポーネントへデータを受け渡してみましょう。
// <input>タグに @input="updateField()" を追記
<template>
<div class="relative pb-4">
<label :for="name" class="text-blue-500 pt-2 uppercase text-xs font-bold absolute">{{ label }}</label>
<input :id="name" type="text" class="pt-8 w-full text-gray-900 border-b pb-2 focus:outline-none focus:border-blue-400" :placeholder="placeholder" v-model="value" @input="updateField()">
</div>
</template>
<script>
export default {
name: "InputField",
props: [
'name', 'label', 'placeholder'
],
data() {
return {
value: ''
}
},
// 以下の methods:{} 部分を追加
methods: {
updateField() {
this.$emit('update:field', this.value);
}
}
}
</script>
最後に、views/ContactsCreate.vue に @update:field="form.name = $event"
等を<input-field/>
の中に書いて終わりです。<input-field/>
のコンポーネントが4つある感じになりますね。
<template>
<div>
<form>
<input-field name="name" label="お名前" placeholder="例:山田ルイ53世" @update:field="form.name = $event"/>
<input-field name="email" label="メールアドレス" placeholder="例:test@test.jp" @update:field="form.email = $event"/>
<input-field name="company" label="組織名" placeholder="例:藤原カンパニー" @update:field="form.company = $event"/>
<input-field name="birthday" label="生年月日" placeholder="例:MM/DD/YYYY" @update:field="form.birthday = $event"/>
<div class="flex justify-end relative pb-4">
<button class="py-2 px-4 text-blue-500 border border-blue-500 mr-5 hover:bg-gray-100 hover:border-blue-300 rounded">キャンセル</button>
<button class="bg-blue-500 border border-blue-500 py-2 px-4 text-white hover:bg-blue-400 mr-1 rounded">新規登録</button>
</div>
</form>
</div>
</template>
<script>
import InputField from "../components/InputField";
export default {
name: "ContactsCreate",
components: {
InputField,
},
data() {
return {
form: {
'name': '',
'email': '',
'company': '',
'birthday': ''
}
}
}
}
</script>
<style scoped>
</style>
いかがでしたでしょうか? いろいろと足らない部分は多々ございますが、一旦ここで筆を置かせていただき、自分の担当であります、洗濯に移ります。明日も朝から洗濯するぞ!
tailwindcss は、一見カオティックな印象ですが、慣れるとそうでもないようなところがなかなか面白いですね!
それでは皆様、
Merry Christmas and Happy New year
良いお年を〜