LoginSignup
5

More than 3 years have passed since last update.

posted at

updated at

Vue.js の フォーム とか tailwindcss とか Vue Router とか コンポーネント とか

この記事は、静岡 Advent Calendar 2019 の25日目の記事です。

はじめに

静岡の勉強会事情等々、皆様がいろいろと書いてくださっているので、自分はそちらの内容はまたの機会にすることにして、当初、Laravel を用いてテストについて何か書こうかなと思っておりました。

が、ちょっとばかり路線変更しまして、Vue.js の フォーム を中心に時間が許す限り何か思うがままに書こうかなと思います。
テストにつきましては、また別の機会にでも書きます。sqlite を使ってやるよくあるやつです。大した内容ではございません。
それから、次点として当初考えておりました、Laravel で過去に作った簡単なプロジェクトを Symfony(完全未経験) で書き直す。というものも考えてはおりましたが、風邪で昨日まで1週間寝込むという愚行を犯した自分には難しかったです。(今のところ娘に移っていないようなのが救いだったり...)

うだうだと書きましたが、それでは、早速本題に入りますよ。

今回は下記スクリーンショットのような form を想定しております。

スクリーンショット 2019-12-24 16.44.08.png

環境につきましては、バックエンドに 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 Routertailwindcss を入れる

## Using npm
npm install vue-router tailwindcss --save-dev

## Using Yarn
yarn add vue-router tailwindcss --save-dev

package.json に、下記のものが追加されていればOKです。

package.json
"devDependencies": {
    "tailwindcss": "^1.1.4",
    "vue": "^2.5.7",       
    "vue-router": "^3.1.3"
},

あともう少しです。

CSS に tailwindcss を追加します

resources/assets/sass/app.scss に下記の内容を書きます。

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 の場合)

webpack.mix.js
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 の場合)

webpack.mix.js
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>の中を、下記のように変更します。

resources/views/layouts/app.blade.php
<!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 を読み込むように記載しています。

resources/views/home.blade.php
@extends('layouts.app')

@section('content')
    <App></App>
@endsection

続きまして、resources/assets/js/components/App.vue も下記のように書きます。
ポイントは、<router-link to="/contacts/create">の箇所です。
/contacts/create のURLに遷移するように設定します。 ( Vue Router用の設定 )

resources/assets/js/components/App.vue
<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 から修正していきますね。

下記のようにしておきましょう。

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 のコンポーネントが表示されるようにします。

resources/assets/js/router.js

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 の値を受け渡すところまで書いてみます。

views/ContactsCreate.vue
<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' を指定しております。

components/InputField.vue
<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 に値が受け渡されたのが確認できるかと思います。

次は、今やったこととは反対に、子コンポーネントから親コンポーネントへデータを受け渡してみましょう。

components/InputField.vue
// <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つある感じになりますね。

views/ContactsCreate.vue
<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

良いお年を〜

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5