# カスタムイベント

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

# イベント名

コンポーネントやプロパティとは違い、イベント名の大文字と小文字は自動的に変換されません。その代わり発火されるイベント名とイベントリスナ名は全く同じにする必要があります。例えばキャメルケース(camelCase)のイベント名でイベントを発火した場合:

this.$emit('myEvent')
1

ケバブケース(kebab-case)でリスナ名を作っても何も起こりません:

<!-- Won't work -->
<my-component @my-event="doSomething"></my-component>
1
2

イベント名は JavaScript 内で変数やプロパティ名として扱われることはないので、キャメルケース(camelCase)またはパスカルケース(PascalCase)を使用する理由はありません。 さらに DOM テンプレート内の v-on イベントリスナは自動的に小文字に変換されます ( HTML が大文字と小文字を判別しないため)。このため @myEvent@myevent になり myEvent にリスナが反応することができなくなります。

こういった理由から いつもケバブケース(kebab-case)を使うこと をお薦めします。

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

発行されたイベントは、 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() {
      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:foo="bar"></my-component>
1

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

const app = Vue.createApp({})

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

# 複数の 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
const app = Vue.createApp({})

app.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  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
19

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 = "var" が設定されているためです。

<my-component v-model.capitalize="bar"></my-component>
1
app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  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

プロパティを設定したので、 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: () => ({})
    }
  },
  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

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

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

Deployed on Netlify.
最終更新日: 3/20/2021, 4:21:43 PM