9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

静岡Advent Calendar 2019

Day 25

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

Last updated at Posted at 2019-12-24

この記事は、静岡 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

良いお年を〜

9
5
0

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
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?