|
| 1 | +# Custom Events |
| 2 | + |
| 3 | +> This page assumes you've already read the [Components Basics](components.md). Read that first if you are new to components. |
| 4 | +
|
| 5 | +## Event Names |
| 6 | + |
| 7 | +Unlike components and props, event names don't provide any automatic case transformation. Instead, the name of an emitted event must exactly match the name used to listen to that event. For example, if emitting a camelCased event name: |
| 8 | + |
| 9 | +```js |
| 10 | +this.$emit('myEvent') |
| 11 | +``` |
| 12 | + |
| 13 | +Listening to the kebab-cased version will have no effect: |
| 14 | + |
| 15 | +```html |
| 16 | +<!-- Won't work --> |
| 17 | +<my-component v-on:my-event="doSomething"></my-component> |
| 18 | +``` |
| 19 | + |
| 20 | +Unlike components and props, event names will never be used as variable or property names in JavaScript, so there's no reason to use camelCase or PascalCase. Additionally, `v-on` event listeners inside DOM templates will be automatically transformed to lowercase (due to HTML's case-insensitivity), so `v-on:myEvent` would become `v-on:myevent` -- making `myEvent` impossible to listen to. |
| 21 | + |
| 22 | +For these reasons, we recommend you **always use kebab-case for event names**. |
| 23 | + |
| 24 | +## `v-model` arguments |
| 25 | + |
| 26 | +By default, `v-model` on a component uses `modelValue` as the prop and `update:modelValue` as the event. We can modify these names passing an argument to `v-model`: |
| 27 | + |
| 28 | +```html |
| 29 | +<my-component v-model:foo="bar"></my-component> |
| 30 | +``` |
| 31 | + |
| 32 | +In this case, child component will expect a `foo` prop and emits `update:foo` event to sync: |
| 33 | + |
| 34 | +```js |
| 35 | +const app = Vue.createApp({}) |
| 36 | + |
| 37 | +app.component('my-component', { |
| 38 | + props: { |
| 39 | + foo: String |
| 40 | + }, |
| 41 | + template: ` |
| 42 | + <input |
| 43 | + type="text" |
| 44 | + v-bind:value="foo" |
| 45 | + v-on:input="$emit('update:foo', $event.target.value)"> |
| 46 | + ` |
| 47 | +}) |
| 48 | +``` |
| 49 | + |
| 50 | +Note that this enables multiple v-model bindings on the same component, each syncing a different prop, without the need for extra options in the component: |
| 51 | + |
| 52 | +```html |
| 53 | +<my-component v-model:foo="bar" v-model:name="userName"></my-component> |
| 54 | +``` |
| 55 | + |
| 56 | +## Handling `v-model` modifiers |
| 57 | + |
| 58 | +In 2.x, we have hard-coded support for modifiers like `.trim` on component `v-model`. However, it would be more useful if the component can support custom modifiers. In 3.x, modifiers added to a component `v-model` will be provided to the component via the modelModifiers prop: |
| 59 | + |
| 60 | +```html |
| 61 | +<my-component v-model.capitalize="bar"></my-component> |
| 62 | +``` |
| 63 | + |
| 64 | +```js |
| 65 | +app.component('my-component', { |
| 66 | + props: { |
| 67 | + modelValue: String, |
| 68 | + modelModifiers: { |
| 69 | + default: () => ({}) |
| 70 | + } |
| 71 | + }, |
| 72 | + template: ` |
| 73 | + <input type="text" |
| 74 | + v-bind:value="modelValue" |
| 75 | + v-on:input="$emit('update:modelValue', $event.target.value)"> |
| 76 | + `, |
| 77 | + created() { |
| 78 | + console.log(this.modelModifiers) // { capitalize: true } |
| 79 | + } |
| 80 | +}) |
| 81 | +``` |
| 82 | + |
| 83 | +We can check `modelModifier` object keys and write a handler to change the emitted value. In the code below we will capitalize the string: |
| 84 | + |
| 85 | +```html |
| 86 | +<div id="app"> |
| 87 | + <my-component v-model.capitalize="myText"></my-component> |
| 88 | + {{ myText }} |
| 89 | +</div> |
| 90 | +``` |
| 91 | + |
| 92 | +```js |
| 93 | +const app = Vue.createApp({ |
| 94 | + data() { |
| 95 | + return { |
| 96 | + myText: '' |
| 97 | + } |
| 98 | + } |
| 99 | +}) |
| 100 | + |
| 101 | +app.component('my-component', { |
| 102 | + props: { |
| 103 | + modelValue: String, |
| 104 | + modelModifiers: { |
| 105 | + default: () => ({}) |
| 106 | + } |
| 107 | + }, |
| 108 | + methods: { |
| 109 | + emitValue(e) { |
| 110 | + let value = e.target.value |
| 111 | + if (this.modelModifiers.capitalize) { |
| 112 | + value = value.charAt(0).toUpperCase() + value.slice(1) |
| 113 | + } |
| 114 | + this.$emit('update:modelValue', value) |
| 115 | + } |
| 116 | + }, |
| 117 | + template: `<input |
| 118 | + type="text" |
| 119 | + v-bind:value="modelValue" |
| 120 | + v-on:input="emitValue">` |
| 121 | +}) |
| 122 | + |
| 123 | +app.mount('#app') |
| 124 | +``` |
| 125 | + |
| 126 | +For `v-model` with arguments, the generated prop name will be `arg + "Modifiers"`: |
| 127 | + |
| 128 | +```html |
| 129 | +<my-component v-model:foo.capitalize="bar"></my-component> |
| 130 | +``` |
| 131 | + |
| 132 | +```js |
| 133 | +app.component('my-component', { |
| 134 | + props: ['foo', 'fooModifiers'], |
| 135 | + template: ` |
| 136 | + <input type="text" |
| 137 | + v-bind:value="foo" |
| 138 | + v-on:input="$emit('update:foo', $event.target.value)"> |
| 139 | + `, |
| 140 | + created() { |
| 141 | + console.log(this.fooModifiers) // { capitalize: true } |
| 142 | + } |
| 143 | +}) |
| 144 | +``` |
0 commit comments