Event Handling
We can use the v-on directive, which we typically shorten to the @ symbol, to listen to DOM events and run some JavaScript when they’re triggered. The usage would be v-on:click=»handler» or with the shortcut, @click=»handler» .
The handler value can be one of the following:
- Inline handlers: Inline JavaScript to be executed when the event is triggered (similar to the native onclick attribute).
- Method handlers: A property name or path that points to a method defined on the component.
Inline Handlers
Inline handlers are typically used in simple cases, for example:
const count = ref(0)
data() return count: 0 > >
button @click="count++">Add 1button> p>Count is: < count >>p>
Method Handlers
The logic for many event handlers will be more complex though, and likely isn’t feasible with inline handlers. That’s why v-on can also accept the name or path of a component method you’d like to call.
const name = ref('Vue.js') function greet(event) alert(`Hello $name.value>!`) // `event` is the native DOM event if (event) alert(event.target.tagName) > >
data() return name: 'Vue.js' > >, methods: greet(event) // `this` inside methods points to the current active instance alert(`Hello $this.name>!`) // `event` is the native DOM event if (event) alert(event.target.tagName) > > >
button @click="greet">Greetbutton>
A method handler automatically receives the native DOM Event object that triggers it — in the example above, we are able to access the element dispatching the event via event.target.tagName .
Method vs. Inline Detection
The template compiler detects method handlers by checking whether the v-on value string is a valid JavaScript identifier or property access path. For example, foo , foo.bar and foo[‘bar’] are treated as method handlers, while foo() and count++ are treated as inline handlers.
Calling Methods in Inline Handlers
Instead of binding directly to a method name, we can also call methods in an inline handler. This allows us to pass the method custom arguments instead of the native event:
function say(message) alert(message) >
methods: say(message) alert(message) > >
button @click="say('hello')">Say hellobutton> button @click="say('bye')">Say byebutton>
Accessing Event Argument in Inline Handlers
Sometimes we also need to access the original DOM event in an inline handler. You can pass it into a method using the special $event variable, or use an inline arrow function:
button @click="warn('Form cannot be submitted yet.', $event)"> Submit button> button @click="(event) => warn('Form cannot be submitted yet.', event)"> Submit button>
function warn(message, event) // now we have access to the native event if (event) event.preventDefault() > alert(message) >
methods: warn(message, event) // now we have access to the native event if (event) event.preventDefault() > alert(message) > >
Event Modifiers
It is a very common need to call event.preventDefault() or event.stopPropagation() inside event handlers. Although we can do this easily inside methods, it would be better if the methods can be purely about data logic rather than having to deal with DOM event details.
To address this problem, Vue provides event modifiers for v-on . Recall that modifiers are directive postfixes denoted by a dot.
a @click.stop="doThis">a> form @submit.prevent="onSubmit">form> a @click.stop.prevent="doThat">a> form @submit.prevent>form> div @click.self="doThat">. div>
Order matters when using modifiers because the relevant code is generated in the same order. Therefore using @click.prevent.self will prevent click’s default action on the element itself and its children, while @click.self.prevent will only prevent click’s default action on the element itself.
The .capture , .once , and .passive modifiers mirror the options of the native addEventListener method:
div @click.capture="doThis">. div> a @click.once="doThis">a> div @scroll.passive="onScroll">. div>
The .passive modifier is typically used with touch event listeners for improving performance on mobile devices.
Do not use .passive and .prevent together, because .passive already indicates to the browser that you do not intend to prevent the event’s default behavior, and you will likely see a warning from the browser if you do so.
Key Modifiers
When listening for keyboard events, we often need to check for specific keys. Vue allows adding key modifiers for v-on or @ when listening for key events:
input @keyup.enter="submit" />
You can directly use any valid key names exposed via KeyboardEvent.key as modifiers by converting them to kebab-case.
input @keyup.page-down="onPageDown" />
In the above example, the handler will only be called if $event.key is equal to ‘PageDown’ .
Key Aliases
Vue provides aliases for the most commonly used keys:
- .enter
- .tab
- .delete (captures both «Delete» and «Backspace» keys)
- .esc
- .space
- .up
- .down
- .left
- .right
System Modifier Keys
You can use the following modifiers to trigger mouse or keyboard event listeners only when the corresponding modifier key is pressed:
On Macintosh keyboards, meta is the command key (⌘). On Windows keyboards, meta is the Windows key (⊞). On Sun Microsystems keyboards, meta is marked as a solid diamond (◆). On certain keyboards, specifically MIT and Lisp machine keyboards and successors, such as the Knight keyboard, space-cadet keyboard, meta is labeled “META”. On Symbolics keyboards, meta is labeled “META” or “Meta”.
input @keyup.alt.enter="clear" /> div @click.ctrl="doSomething">Do somethingdiv>
Note that modifier keys are different from regular keys and when used with keyup events, they have to be pressed when the event is emitted. In other words, keyup.ctrl will only trigger if you release a key while holding down ctrl . It won’t trigger if you release the ctrl key alone.
.exact Modifier
The .exact modifier allows control of the exact combination of system modifiers needed to trigger an event.
button @click.ctrl="onClick">Abutton> button @click.ctrl.exact="onCtrlClick">Abutton> button @click.exact="onClick">Abutton>
Mouse Button Modifiers
- .left
- .right
- .middle
These modifiers restrict the handler to events triggered by a specific mouse button.
Component Events
This page assumes you’ve already read the Components Basics. Read that first if you are new to components.
Emitting and Listening to Events
A component can emit custom events directly in template expressions (e.g. in a v-on handler) using the built-in $emit method:
button @click="$emit('someEvent')">click mebutton>
The $emit() method is also available on the component instance as this.$emit() :
export default methods: submit() this.$emit('someEvent') > > >
The parent can then listen to it using v-on :
MyComponent @some-event="callback" />
The .once modifier is also supported on component event listeners:
MyComponent @some-event.once="callback" />
Like components and props, event names provide an automatic case transformation. Notice we emitted a camelCase event, but can listen for it using a kebab-cased listener in the parent. As with props casing, we recommend using kebab-cased event listeners in templates.
Unlike native DOM events, component emitted events do not bubble. You can only listen to the events emitted by a direct child component. If there is a need to communicate between sibling or deeply nested components, use an external event bus or a global state management solution.
Event Arguments
It’s sometimes useful to emit a specific value with an event. For example, we may want the component to be in charge of how much to enlarge the text by. In those cases, we can pass extra arguments to $emit to provide this value:
button @click="$emit('increaseBy', 1)"> Increase by 1 button>
Then, when we listen to the event in the parent, we can use an inline arrow function as the listener, which allows us to access the event argument:
MyButton @increase-by="(n) => count += n" />
Or, if the event handler is a method:
MyButton @increase-by="increaseCount" />
Then the value will be passed as the first parameter of that method:
methods: increaseCount(n) this.count += n > >
function increaseCount(n) count.value += n >
All extra arguments passed to $emit() after the event name will be forwarded to the listener. For example, with $emit(‘foo’, 1, 2, 3) the listener function will receive three arguments.
Declaring Emitted Events
A component can explicitly declare the events it will emit using the defineEmits() macro emits option :
script setup> defineEmits(['inFocus', 'submit']) script>
The $emit method that we used in the isn’t accessible within the section of a component, but defineEmits() returns an equivalent function that we can use instead:
script setup> const emit = defineEmits(['inFocus', 'submit']) function buttonClick() emit('submit') > script>
The defineEmits() macro cannot be used inside a function, it must be placed directly within , as in the example above.
If you’re using an explicit setup function instead of , events should be declared using the emits option, and the emit function is exposed on the setup() context:
export default emits: ['inFocus', 'submit'], setup(props, ctx) ctx.emit('submit') > >
As with other properties of the setup() context, emit can safely be destructured:
export default emits: ['inFocus', 'submit'], setup(props, emit >) emit('submit') > >
export default emits: ['inFocus', 'submit'] >
The emits option also supports an object syntax, which allows us to perform runtime validation of the payload of the emitted events:
script setup> const emit = defineEmits( submit(payload) // return `true` or `false` to indicate // validation pass / fail > >) script>
If you are using TypeScript with , it’s also possible to declare emitted events using pure type annotations:
script setup lang="ts"> const emit = defineEmits< (e: 'change', id: number): void (e: 'update', value: string): void >>() script>
export default emits: submit(payload) // return `true` or `false` to indicate // validation pass / fail > > >
Although optional, it is recommended to define all emitted events in order to better document how a component should work. It also allows Vue to exclude known listeners from fallthrough attributes, avoiding edge cases caused by DOM events manually dispatched by 3rd party code.
If a native event (e.g., click ) is defined in the emits option, the listener will now only listen to component-emitted click events and no longer respond to native click events.
Events Validation
Similar to prop type validation, an emitted event can be validated if it is defined with the object syntax instead of the array syntax.
To add validation, the event is assigned a function that receives the arguments passed to the this.$emit emit call and returns a boolean to indicate whether the event is valid or not.
script setup> const emit = defineEmits( // No validation click: null, // Validate submit event submit: ( email, password >) => if (email && password) return true > else console.warn('Invalid submit event payload!') return false > > >) function submitForm(email, password) emit('submit', email, password >) > script>
export default 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 >) > > >
Get value from emit in input field with Vue
I know this has a simple answer but I appear to be stuck. I have an upload image input in a form. Following several tutorials, I have successfully created the upload method. My issue is once the image is uploaded to Firestore storage I use this.$emit(‘imgurl’, downloadURL) My problem is I do not know how to get that value so when the user submits the form the url gets added to the database. Parts of the code: HTML:
>%
detectFiles (fileList) < Array.from(Array(fileList.length).keys()).map( x =>< this.upload(fileList[x]) >) >, upload (file) < var storage = firebase.storage(); this.uploadTask = storage.ref('avatars/'+file.name).put(file); >
watch: < uploadTask: function() < this.uploadTask.on('state_changed', sp =>< this.progressUpload = Math.floor(sp.bytesTransferred / sp.totalBytes * 100) >, null, () => < this.uploadTask.snapshot.ref.getDownloadURL().then(downloadURL =>< this.downloadURL = downloadURL this.$emit('imgurl', downloadURL) >) >) > >
Add to the database:
db.collection('teams').add(< team_name: this.team_name, team_id: this.team_id, bio: this.bio, url: this.imgurl, >).then(() => < this.$router.push(< name: 'Admin' >) >).catch(err => < console.log(err) >)
- javascript
- vue.js
- google-cloud-firestore
- vue-component
Пользовательские события
Подразумевается, что вы уже изучили и разобрались с разделом Основы компонентов. Если нет — прочитайте его сначала.
Стиль именования событий
В отличие от компонентов и входных параметров, имена событий не предоставляют никакой автоматической трансформации стиля именования события. Вместо этого, имя генерируемого события должно точно соответствовать имени, используемому при прослушивании события. К примеру, если генерируем событие с именем в camelCase:
this.$emit('myEvent')
Прослушивание kebab-cased версии этого имени не будет иметь никакого эффекта:
my-component v-on:my-event="doSomething"> my-component>
В отличие от компонентов и входных параметров, имена событий никогда не будут использоваться в качестве имён переменных или имён свойств в JavaScript, поэтому нет причин использовать camelCase или PascalCase. Кроме того, директивы прослушивания событий v-on внутри DOM-шаблонов автоматически преобразуются в нижний регистр (из-за нечувствительности HTML к регистру), поэтому v-on:myEvent станет v-on:myevent — что делает прослушивание события myEvent невозможным.
По этим причинам мы рекомендуем всегда использовать kebab-case для имён событий.
Настройка v-model у компонента
По умолчанию v-model на компоненте использует входной параметр value и событие input . Но некоторые типы полей, такие как чекбоксы или радиокнопки, могут использовать атрибут value для других целей. Использование опции model позволит избежать конфликта в таких случаях:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
Теперь, когда используем v-model на этом компоненте:
base-checkbox v-model="lovingVue"> base-checkbox>
значение lovingVue будет передано во входном параметре checked . А обновляться свойство lovingVue будет когда сгенерирует событие change с новым значением.
Обратите внимание, что вам всё равно нужно объявлять входной параметр checked в опции props компонента.
Подписка на нативные события в компонентах
Иногда нужно подписаться на нативные события браузера на корневом элементе компонента. В таких случаях можно применять модификатор .native для v-on :
base-input v-on:focus.native="onFocus"> base-input>
Иногда это может быть полезно, но это не очень хорошая идея, когда вы пытаетесь прослушивать очень специфичный элемент, к примеру . Например, компонент можно переделать так, чтобы корневой элемент был фактически элементом :
label>
{{ label }}
input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
label>
В этом случае слушатель .native в родителе просто тихо перестанет работать. Ошибок не будет, но обработчик onFocus не будет вызываться, когда мы этого ожидаем.
Для решения этой проблемы Vue предоставляет свойство $listeners , содержащее объект всех слушателей, которые используются на компоненте. Например:
{
focus: function (event) { /* . */ }
input: function (value) { /* . */ },
}
Используя свойство $listeners , вы можете передать все слушатели событий на компоненте на определённый дочерний элемент с помощью v-on=»$listeners» . Для таких элементов как , вы также можете захотеть работоспособности с v-model , для чего бывает полезно создать новое вычисляемое свойство для слушателей, например inputListeners указанный ниже:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function ( ) {
var vm = this
// `Object.assign` объединяет объекты вместе, чтобы получить новый объект
return Object.assign({},
// Мы добавляем все слушатели из родителя
this.$listeners,
// Затем мы можем добавить собственные слушатели или
// перезаписать поведение некоторых существующих.
{
// Это обеспечит, что будет работать v-model на компоненте
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
{{ label }}
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
`
})
Теперь компонент стал полностью прозрачной обёрткой, что означает, что его можно использовать точно также, как и обычный элемент : все те же атрибуты и слушатели будут работать без указания модификатора .native .
Модификатор .sync
В некоторых случаях нам может понадобиться «двусторонняя привязка» для входных параметров. К сожалению, настоящая двусторонняя привязка может создавать проблемы с поддержкой, поскольку дочерние компоненты смогут мутировать состояние родителя без источника, который является очевидным как у родителя, так и у потомков.
Поэтому вместо этого, мы рекомендуем генерировать события с определённым шаблоном имени update:myPropName . Например, в гипотетическом компоненте с входным параметром title , мы можем сообщить о намерении присвоить новое значение:
this.$emit('update:title', newTitle)
Затем, родитель может прослушать это событие и обновить локальное свойство, если захочет. Например:
text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
> text-document>
Для удобства мы предлагаем краткую запись для этого шаблона с помощью модификатора .sync :
text-document v-bind:title.sync="doc.title"> text-document>
Обратите внимание, что v-bind с модификатором .sync не работает с выражениями (например, v-bind:title.sync=”doc.title + ‘!’” не будет работать). Вместо этого нужно указывать только имя свойства, которое хотите привязать, аналогично v-model .
Модификатор .sync также может использоваться вместе с v-bind при использовании объектной записи, чтобы сразу устанавливать значения нескольких входных параметров:
text-document v-bind.sync="doc"> text-document>
Это передаёт каждое свойство в объекте doc (например, title ) в качестве индивидуальных входных параметров, а затем добавляет слушатели событий v-on для каждого из них.
Использование v-bind.sync с литеральным объектом, например таким как v-bind.sync=”{ title: doc.title }” , не будет работать, потому что необходимо будет учитывать слишком много различных пограничных случаев при анализе подобного сложного выражения.
Обнаружили ошибку или хотите добавить что-то своё в документацию? Измените эту страницу на GitHub! Опубликовано на Netlify .