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


# 使えそうなケース

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



# 子コンポーネント

```vue
// components/ChildComponent.vue

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

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



# 親コンポーネント

```vue
// 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>
```

- 同様に

```vue
// 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>
```



## 引数も送りたいとき

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

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

# 双方向バインディングを実現する
- 簡単なloginフォームを想定します。
```vuejs
// 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行を１行で書ける
    // :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