0
0

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.

子コンポーネントでは$emitさせて親コンポーネントでふるまいを変える

Last updated at Posted at 2022-02-26

使えそうなケース

  • コンポーネントをロジックとスタイリングを分割させているときに、スタイリング用のコンポーネントでは見た目のレイアウトに専念させ、ロジック部分は$emitさせて親のコンポーネントのメソッドを差し込める。
  • たとえば、一方の親コンポーネントではボタンをクリックしたら"GOOD"をalertさせ、もう一方の親コンポーネントでは"BAD"をalertさせるには、以下のように実装する

子コンポーネント

// components/ChildComponent.vue

<template>
  <button @click="clickButton"></button>
</template>

<script>
  export default = {
      method: {
          clickButton() {
              this.$emit('click-button') // click-buttonというカスタムイベントを作る
          }
      }
  }
</script>

親コンポーネント

// components/goodComponent.vue

<template>
  <child-component
  	@click-button="alertGood" // カスタムイベントなので@(v-on)でイベントをハンドルする
  ></child-component>
</template>

<script>
  import ChildCompoent from '@/components/ChildComponent'  
    
  export default {
      components: {
          Childcomponent
      },
      methods: {
          alertGood() {
              alert('GOOD');
          }
      }
  }
</script>
  • 同様に
// components/BadComponent.vue

<template>
  <child-component
  	@click-button="alertBad"
  ></child-component>
</template>

<script>
  import ChildCompoent from '@/components/ChildComponent'  
    
  export default {
      components: {
          Childcomponent
      },
      methods: {
          alertBad() {
              alert('BAD');
          }
      }
  }
</script>

引数も送りたいとき

// child
method: {
    clickButton() {
    	this.$emit('click-button', "data") // $emitの引数に入れる
    }
}

//parent
<child-component @click-button="alertData" 
></child-component>
...
methods: {
    alertData(data) { // ここで受け取る
    	alert(data);
    }
}

双方向バインディングを実現する

  • 簡単なloginフォームを想定します。
// ChildComponents.vue

<template>  
<form>
    <input type="email" v-model="localEmail">
    <input type="password" v-model="localPassword">
    <button @click="onSubmit">
  </form>
</template>

<script>
export default {
  props: {
    email: {
      type: String,
      required: true,
    },
    password: {
      type: String,
      required: true,
    },
  },
  computed: { 
  // 算出プロパティにgetterとsetterを作ってv-modelをつくる
    localEmail: {
      get() {
        return this.email
      },
      set(val) {
        // 親側でupdateイベントで発火し、双方向バインディングを作れる
        this.$emit('update:email', val)
      },
    },
    localPassword: {
      get() {
        return this.password
      },
      set(val) {
        this.$emit('update:password', val)
      },
    },

  methods: {
    onSubmit(event) {
      this.$emit('submit', event); // カスタムイベントsubmitを登録
    }
  }
}
</script>

vue.js
// ParentComponent.vue

<template>
  <child-component
    // .sync修飾子で以下の2行を1行で書ける
    // :email="form.email"
    // @update:email="form.email = $event"
    :email.sync="form.email"
    :password.sync="form.password"
    @submit.prevent.stop="login"
  ></child-component>
</template>

<script>
import ChildComponent from '@/components/ChildComponent'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      form: {
        email: '',
        password: '',
      },
    }
  },
  methods: {
    login(event) {
      // apiにpostとか...
    }
  },

参考資料

https://jp.vuejs.org/v2/guide/components-custom-events.html
https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6-Vue-js-MIO-ebook/dp/B07D9BYHMZ

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?