# カスタムイベント

このページは コンポーネントの基本 が読まれていることが前提となっています。 コンポーネントを扱った事のない場合はこちらのページを先に読んでください。

# イベント名

コンポーネントやプロパティと同じように、イベント名は大文字と小文字を自動的に変換します。子コンポーネントからキャメルケースでイベントを発行すると、親コンポーネントではケバブケースのリスナを追加できるようになります:

this.$emit('myEvent')
1
<my-component @my-event="doSomething"></my-component>
1

プロパティの形式 と同じように、DOM 内テンプレートを使っている場合は、ケバブケースのイベントリスナを使うことをお勧めします。文字列テンプレートを使っている場合は、この制約は適用されません。

# カスタムイベントの定義

Vue School でカスタムイベントの定義についての無料ビデオを視聴する

発行されたイベントは、 emits オプションを介して、コンポーネントで定義することが出来ます。

app.component('custom-form', {
  emits: ['inFocus', 'submit']
})
1
2
3

ネイティブイベント(例、 click など)が emits オプションで定義されている場合、ネイティブイベントリスナの 代わりに コンポーネントのイベントが使われます。

TIP

コンポーネントの動作を実証するために、全ての発行されたイベントを定義することをお勧めします。

# 発行されたイベントを検証する

プロパティの型検証と同様に、発行されたイベントは、配列構文ではなくオブジェクト構文で定義されている場合に検証できます。

検証を追加するために、イベントには、 $emit 呼び出しに渡された引数を受け取る関数が割り当てられ、イベントが有効かどうかを示す真偽値を返します。

app.component('custom-form', {
  emits: {
    // No validation
    click: null,

    // Validate submit event
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# v-model の引数

デフォルトでは、コンポーネントの v-model はプロパティとして modelValue を使用し、イベントとして update:modelValue を使用します。v-model 引数を渡してこれらの名前の変更が出来ます。

<my-component v-model:title="bookTitle"></my-component>
1

この場合、子コンポーネントは title プロパティを期待し、同期するために update:title イベントを発行します。

app.component('my-component', {
  props: {
    title: String
  },
  emits: ['update:title'],
  template: `
    <input
      type="text"
      :value="title"
      @input="$emit('update:title', $event.target.value)">
  `
})
1
2
3
4
5
6
7
8
9
10
11
12

# 複数の v-model のバインディング

以前 v-model 引数 で学習した特定のプロパティとイベントをターゲットにする機能を活用することで、単一のコンポーネントインスタンスに対して、複数の v-model バインディングを作成できるようになりました。

それぞれの v-model は、コンポーネントに追加オプションを必要とせず、異なるプロパティに同期します。

<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>
1
2
3
4
app.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  emits: ['update:firstName', 'update:lastName'],
  template: `
    <input
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)">

    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)">
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

See the Pen Multiple v-models by Vue (@Vue) on CodePen.

# v-model 修飾子の処理

フォーム入力バインディングについて学習していたときに、 v-model組み込み修飾子 - .trim.number、および .lazy があることがわかりました。ただし、場合によっては、独自のカスタム修飾子を追加することもできます。

v-model バインディングによって提供される文字列の最初の文字を大文字にするカスタム修飾子の例、capitalize を作成してみましょう。

コンポーネント v-model に追加された修飾子は、modelModifiers プロパティを介してコンポーネントに提供されます。以下の例では、デフォルトで空のオブジェクトになる modelModifiers プロパティを含むコンポーネントを作成しました。

コンポーネントの created ライフサイクルフックがトリガーされると、modelModifiers プロパティには capitalize が含まれ、その値は true になります。これは、 v-model バインディングに v-model.capitalize="myText" が設定されているためです。

<my-component v-model.capitalize="myText"></my-component>
1
app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  emits: ['update:modelValue'],
  template: `
    <input type="text"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)">
  `,
  created() {
    console.log(this.modelModifiers) // { capitalize: true }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

プロパティを設定したので、 modelModifiers オブジェクトのキーを確認し、発行された値を変更するハンドラーを記述できます。以下のコードでは、 <input /> 要素が input イベントを発生させるたびに文字列を大文字にします。

<div id="app">
  <my-component v-model.capitalize="myText"></my-component>
  {{ myText }}
</div>
1
2
3
4
const app = Vue.createApp({
  data() {
    return {
      myText: ''
    }
  }
})

app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  emits: ['update:modelValue'],
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  },
  template: `<input
    type="text"
    :value="modelValue"
    @input="emitValue">`
})

app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

引数を持つ v-model バインディングの場合、生成されるプロパティ名は arg + "Modifiers" になります。

<my-component v-model:description.capitalize="myText"></my-component>
1
app.component('my-component', {
  props: ['description', 'descriptionModifiers'],
  emits: ['update:description'],
  template: `
    <input type="text"
      :value="description"
      @input="$emit('update:description', $event.target.value)">
  `,
  created() {
    console.log(this.descriptionModifiers) // { capitalize: true }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12