Vue.js

【Vue.js】コンポーネント間のデータのやりとりをログインフォームで説明してみた

全体概要

親 → Login.vue
子 → FormButton.vue、FormInput.vue、FormSelect.vue

今回は、上記の4つのvueテンプレートを用いて説明します。

親→子へデータを渡す:<propsで受け取る>

親コンポーネントでは、コンポーネント内に属性をキーとしてデータを持たせるだけです。

ここでは、button_nameをキーとしているので、子コンポーネントではbutton_nameで展開できます。

Login.vue

<template>
    <FormButton button_name="ログインする" />
</template>


<script>

import FormButton from "~/components/FormButton.vue"

export default {

    components:{
        FormButton
    }
}
</script>

FormButton.vue

そして子のコンポーネントでは、propsを用いてデータを受け取ります。

Login.vueで定義したbutton_nameをpropsで指定しないと、取得できません。

<template>
    <button type="submit">{{ button_name }}</button>
</template>


<script>

export default {

    props: [ "button_name" ]

}
</script>

子→親の関数を実行:<$emitでイベントを発行>

やりたいことは、

FormButton.vueで定義したbutton要素をクリックしたら、Login.vueで定義しているlogin()を実行させることです。

流れは、

FormButton.vue内のbuttonをクリック

FormButton.vue内で定義したissueEvent()を実行

issueEvent内の$emitで親で発火させるイベント名(第1引数)と渡す値(第2引数)を指定

です。

FormButton.vue

<template>
    <!-- @clickを追加 -->
    <button @click="issueEvent" type="submit">{{ button_name }}</button>
</template>


<script>

export default {

    props: [ button_name ],

    // 追加
    methods:{
        issueEvent(){
            this.$emit("signalEvent")
        }
    }

}
</script>

Login.vue

FormButton.vueの$emitで指定されたsignalEventを実行して、指定した処理login()を実行します。

<template>
    <!-- 親側で子のイベントを検知して、親で定義した関数を実行 -->
    <FormButton button_name="ログインする" @signalEvent="login" />
</template>


<script>

import FormButton from "~/components/FormButton.vue"

export default {

    components:{
        FormButton
    },

     // 追加
    methods:{
        login(){
            // ログイン処理
        }
    }
}
</script>

@signalEvent=”login”は、

子側で、signalEventが発火したら、親側のlogin()を実行してね」、っていう合図です。

今回は、子のbutton要素をクリックした情報を親に渡すだけなので、$emitの第2引数には、何も定義していません。

子→親にデータを渡す:<$emitの第2引数>

ここでは、書き方を2種類紹介します。

ここからは、FormInput.vueを使って説明していきます。

パターン1

FormInput.vue

<template>
    <div class="form-input">
        <label>{{ label }}:</label>
        <input v-model="value" @input="issueEvent" type="text" />
    </div>
</template>


<script>

export default {

    props: ["label", "value"],

    methods: {
        issueEvent(e){
            // 第2引数で親に渡したいデータを指定
            this.$emit("signalEvent",e.target.value)
        }
    }
}
</script>

Login.vue

<div class="form-container">
    <FormInput label="メールアドレス" :value="email" @signalEvent="getEmail"></FormInput>
    <FormInput label="パスワード" :value="password" @signalEvent="getPass"></FormInput>
    <FormButton button_name="ログインする" @signalEvent="login"></FormButton>
</div>

<script>

import FormInput from "~/components/Input.vue"
import FormButton from "~/components/Button.vue"

export default {

    components: {
        FormInput,
        FormButton
    },

    data() {
        return {
            email: null,
            password: null,
        }
    },

    methods: {

        // 引数のvalueは、子コンポーネントの$emitの第2引数で指定した値を取得
        getEmail(value){
            this.email = value
        },
        getPass(value){
            this.password = value
        },

        login() {
            // ログイン処理
        }
    }
}
</script>

valueには、子コンポーネントのthis.$emitで指定した第2引数の値が入ります。

パターン2(推奨:v-modelを使用)

こちらのパターンでは、v-modelを使用するやり方です。v-modelについての理解が必要なので、まずは説明をします。

v-modelとは?
<template>
    <input v-model="email">
</template>

上↑と下↓は、同義です。

<template>
    <input
        v-bind:value="email"
        v-on:input="email = $event.target.value">
</template>

省略形で書くならば、

<template>
    <input
        :value="email"
        @input="email = $event.target.value">
</template>

つまりv-modeの1行は、

・inputイベントによるvalueの変化の取得
・data内のemailのバインド

の両方をしているということです。

FormInput.vue

<template>
    <div class="form-input">
        <label>{{ label }}:</label>
        <input :value="value" @input="issueEvent" type="text" />
    </div>
</template>


<script>
export default {
    props: ["label", "value"],

    methods: {
        issueEvent(e){
            // 親側ではv-modelを使用しておりデフォルトは@inputなので"input"を指定
            this.$emit("input", e.target.value)
        }
    }
}
</script>

Login.vue

v-modelは、@inputを内包しているので、パターン1で記述した@signalEvent=”〜”を書かなくても良いというメリットがあります。

<div class="form-container">
    <FormInput label="メールアドレス" v-model="email"></FormInput>
    <FormInput label="パスワード" v-model="password"></FormInput>
    <FormButton button_name="ログインする" @signalEvent="login"></FormButton>
</div>

<script>

import FormInput from "~/components/Input.vue"
import FormButton from "~/components/Button.vue"

export default {

    components: {
        FormInput,
        FormButton
    },

    data() {
        return {
            email: null,
            password: null,
        }
    },

    methods: {
        login() {
            // ログイン処理
        }
    }
}
</script>

セレクトボックス

Login.vue

<div class="form-container">
    <FormInput label="メールアドレス" v-model="user_name" />
    <span>@</span>
    <FormSelect :options="[
        {name: 'gmail.com', value: '@gmail.com'},
        {name: 'yahoo.co.jp', value: '@yahoo.co.jp'}
    ]" v-model="domain" />
    <FormInput label="パスワード" v-model="password" />
    <FormButton @signalEvent="login" button_name="ログインする" />
</div>

<script>

import FormInput from "~/components/Input.vue"
import FormSelect from "~/components/Select.vue"
import FormButton from "~/components/Button.vue"

export default {

    components: {
        FormInput,
        FormSelect,
        FormButton
    },

    data() {
        return {
            user_name: null,
            domain: null,
            password: null,
        }
    },

    methods: {
        login() {
            // ログイン処理
        }
    }
}
</script>

Form.Select.vue

<template>
    <select @change="issueEvent" >
        <option v-for="option in options"
            :key="option.name"
            :value="option.value"
            :selected="option.name == 'gmail.com'"
        >{{ option.name }}</option>
    </select>
</template>

<script>

export default {

    props: {
        // 中身は統一:[{name: "表示名", value: "値"}]
        options: {
            type: Array,
            required: true
        }
    },

    methods: {
        issueEvent(e){
            this.$emit("input", e.target.value)
        }
    }
}
</script>

今回は、以上です。

他にも良い書き方などあればコメント欄からコメントください!

COMMENT

メールアドレスが公開されることはありません。