retrieve $event object from emit event in parent component - vue.js

I am emitting an event in a child component so I can, if I need to, prevent it from happening in the parent.
Is there a way to retrieve event object so I can prevent it in the parent component like this ?
// child component event
this.$emit('navigationTab');
// parent template event bind
#onTab="on_tab($event)"
// parent handler method
on_tab(event) {
event.preventDefault
// ...logic etc
}
It only works if y pass the event object like this I wish to catch $event directly without sending the object inside the event, which is probably already sent in the emit
// child component event
this.$emit('navigationTab', event);
// parent template bind
#navigationTab="on_tab"
// parent handler method
on_tab(event) {
event.preventDefault
// ...logic etc
}

Event argument is automatically passed as an parameter into handler method (see example bellow)
You can't use preventDefault() on custom events, only on native browser events. And of course you don't need to because there is not any "default" behavior for custom events (emitted with $emit)
const myComponent = Vue.component('myComponent', {
template: `
<div>
<button #click="$emit('myEvent', 'event payload')">Click me!</button>
</div>
`
})
const app = new Vue({
components: {myComponent} ,
template: `
<div>
<myComponent #myEvent="handle" />
</div>
`,
methods: {
handle(event) {
console.log("Event received:", event)
}
}
})
app.$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Related

Vue how to get value from sub component

Here is my use case:
My main page have several sub-components that collect different input from user, finally I want to submit the whole page with all inputs collected. Therefore I want to retrieve the data from sub-component
One option is to use store, but my sub-components are super simple, just some forms, store seems too heavy...
Another option is that I can modify prop, although I know this is bad practice, but this approach looks just perfect....
Is it ok to modify prop if my logic is simple?(just collect inputs from user)Or I have to go for Vuex and store
Expanding on excellent answers from Ifaruki and Andres Foronda, another, related option is the use of the sync modifier on the child component's prop.
Suppose the child component has a prop named name. In the parent component, you can use the sync modifier like this:
<Child :name.sync="childName"></Child>
Then, in the child component, when the value of the name prop should be updated, don't update it directly. Instead, emit an event that follows the naming convention for sync-able props, which is update:nameOfProp. So in our example, the child component would do this:
this.$emit('update:name', newName);
The benefit of the sync modifier is that we don't have to write an event handler function in the parent component--Vue does that for us and updates the variable that is bound to the prop automatically.
You can read more details about the sync modifier in the official docs.
Retreiving data from sub component works with $emit here an exapmle:
//parent copmonent
<template>
<div>
<child #someEvent="someMethod"></child>
</div>
</template>
import child from "path/"
<script>
export default {
components: {
child
},
methods: {
someMethod(data){
console.log(data);
}
}
}
</script>
Child component
<template>
<div>
<button #click="sendEvent">send</button>
</div>
</template>
<script>
export default {
methods: {
sendEvent(){
this.$emit("someEvent", "working");
}
}
}
</script>
$emit takes 2 arguments. The first is the event name and the second one is the data that you send.
The parent just needs to listen with # for that event that being fired.
you can listen an event from child an update the parent data property
//parent component
<div>
<input-name #updateName="eventToUpdateName" /> <!--child component-->
</div>
...
data: () => ({ nameFromChild: '' )},
methods: {
eventToUpdateName(value) {
this.nameFromChild = value; // Update from child value emitted
}
}
...
And in the child component
// Child component
<input v-model="name" />
...
data: () => ({ name: '' }),
// watch for changes in the name property and emit an event, and pass the value to the parent
watch: { name() { this.$emit('updateName', this.name } }
...
Also, You can use a v-model directive and emit 'input' event from child.
//parent component
<div>
<input-name v-model="nameFromChild" /> <!--child component-->
</div>
...
data: () => ({ nameFromChild: '' )}
...
Now in the child component you can have
// Child component
<div>
<input v-model="name" />
</div>
data: () => ({ name: '' }),
props: { value: { type: String, default: '' },
created() { this.name = this.value }, // You can receive a default value
watch: { name() { this.$emit('input', this.name } }
...

How i can run method from child component vuejs

I have parent and chidl component... I need to run child method, when i click on button in parent.
Example code:
Parent
<template>
<child-component></child-component>
<button>Open Modal in child Component (set modal = true in child component)</button>
</template>
Child:
<template>
<div v-if="modal">
<button #click="modal = false">Close</button>
</div>
</template>
<script>
export default {
data() {
return {
modal: false
}
}
}
</script>
You can achieve this via different implementations. The most common one is via emit (another alternative is via dispatching actions if you are using the Redux pattern)
First, you want to catch the even on the parent component and emit an event. So this should be your template.
<template>
<child-component></child-component>
<button #click="click">Open Modal in child Component (set modal = true in child component)</button>
</template>
Then you have to emit an event (from the parent component) on the function called when a click was made.
Something like:
click: function() {
this.$emit('update');
}
Lastly, your child component needs to "hear" that event. You can achieve that with something like that on the created function of the child component:
this.$parent.$on('update', this.updateModal);
this.updateModal is a function on your child component which just flips the value of the boolean.
In vue you can pass a function as a prop. So you could add a function to alternate model within your parent component then pass it into your child so it can be used normally. I'll put a simple example below.
small edit, you can bind a class like a hidden/reveal class using the status which is bound to the modal state
// Html
<div id="app">
<child v-bind:on-click="toggleModal" v-bind:status="modal" />
</div>
// Parent Component
var sample = new Vue({
el: '#app',
data: {
modal: false
},
methods: {
toggleModal() {
this.modal = !this.modal
}
}
});
// Child component with function prop
Vue.component('child', {
props: {
onClick: Function,
status: Boolean
}
template: '<button v-on:click="onClick()">Press Me</div>'
});

Using $emit to call a function

I'm new to vue.js and am trying to understand how $emit can be used. In the following code, every input element except for the last one works as expected.
My assumption in the last input element is that calling $emit with an event name would be the same as calling the function which has that event name, but it doesn't call that function. What is occurring with this $emit?
I've read through Binding Native Events to Components and that shows that it is to be used differently as I'm doing it. All this approach started from me watching a YouTube video (7 Secret Patterns...), specifically at this time https://youtu.be/7lpemgMhi0k?t=21m57s where you can see that usage on the slide.
Here is the code in JSFiddle https://jsfiddle.net/sbtmfweq/
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="test-app">
<input v-model="text1" placeholder="edit me" #keyup.enter="submit"> {{ text1 }}
<br><br>
<input :value="text2" #input="inputEvent" #keyup.enter="submit"> {{ text2 }}
<br><br>
<input :value="text2" #input="inputEvent($event)" #keyup.enter="submit"> {{ text2 }}
<br><br>
<input :value="text2" #input="$emit('inputEvent', $event.target.value)"> {{ text2 }} | {{reversedText2}}
</div>
<script>
new Vue({
el: '#test-app',
data: {
text1: 'text1',
text2: 'text2',
},
methods: {
log: console.log,
submit: function(event) {
console.log("submit -->", event, event.target.value, '<--')
},
inputEvent: function(event) {
console.log("input 2 -->", event.target.value, '<--')
this.text2 = event.target.value
},
},
watch: {
text1: {
handler: function(newValue, oldValue) {
console.log("input 1 -->", newValue, '<-->', oldValue, '<--')
}
}
},
computed: {
reversedText2: function () {
return this.text2.toUpperCase()
}
}
})
</script>
It's just a simple issue. Whenever you are dispatching an event, you have to add listener to it. In $emit, the first param is actually the name of the event.
As written in docs for $emit.
vm.$emit( eventName, […args] )
In the below code, I have added an event listener, using $on. Also I have changed the arg for $emit.
<input :value="text2" #input="$emit('inputEvent', $event)"> {{ text2 }} | {{reversedText2}}
<script>
new Vue({
...
created(){
this.$on('inputEvent', this.inputEvent);
}
...
})
</script>
I hope it helps.
$emit works like this.
Say you have 2 components, <parent> and <child>, where <child> is inside <parent>'s template.
When <child> emits an event, it can be handled by the <parent> component only (unless you register a listener via $on, but that's unrelated to your scenario). In your code, the $emit call is executed within the scope of the parent component (which is the root component instantiated with new Vue()); that is, the parent component is the one that's emitting the event. The root component has no parent so the emit call is pointless.
My assumption in the last input element is that calling $emit with an event name would be the same as calling the function which has that event name, but it doesn't call that function.
This isn't true; the name of the event has no relation to the method registered as a listener for that event in the parent.
it's normal that $emit doesn't work in your case.
In fact, $emit is used for communication between parent and child components. Here, you are using $emit but you haven't defined any parent component.
Here is the docs for the custom events.
Hope it helps!

VueJS Custom directive + emit event

I need to $emit an event from a custom directive.
Is it possible?
directive.js:
vnode.context.$emit("myEvent") // nothing append
vnode.child.$emit("myEvent") // error
vnode.parent.$emit("myEvent") // error
component.vue:
<div v-directive.modifier="binding" #myEvent="method()"></div>
Do you know if it's possible or if there is any trick?
Thanks
A <div> is not a VueComponent, which means it doesn't have an $emit method.
So to make your Vue custom directive emit an event, you will have to do some checking first:
If the directive was used in a Vue custom component, then call $emit() of that component's instance
If the directive was used in a regular DOM element (...because there's no $emit()...), then dispatch a native DOM event using .dispatchEvent.
Luckily, Vue's v-on listeners respond to native custom events.
That should be all. Demo implementation below.
Vue.component('my-comp', {
template: `<input value="click me and check the console" size="40">`
});
Vue.directive('my-directive', {
bind: function (el, binding, vnode) {
// say you want to execute your logic when the element is clicked
el.addEventListener('click', function (e) {
var eventName = 'my-event';
var eventData = {myData: 'stuff - ' + binding.expression}
if (vnode.componentInstance) {
vnode.componentInstance.$emit(eventName, {detail: eventData}); // use {detail:} to be uniform
} else {
vnode.elm.dispatchEvent(new CustomEvent(eventName, {detail: eventData}));
}
})
}
})
new Vue({
el: '#app',
methods: {
handleStuff(e) { console.log('my-event received', e.detail); }
}
})
<script src="https://unpkg.com/vue#2.5.15/dist/vue.min.js"></script>
<div id="app">
<div v-my-directive.modifier="'native element'" #my-event="handleStuff">CLICK ME AND CHECK THE CONSOLE</div>
<hr>
<my-comp v-my-directive.modifier="'custom component'" #my-event="handleStuff"></my-comp>
</div>

How to close dialog by self with vuejs2?

I am writing a dialog component, i don't know how to close dialog by self.
<template>
<div class="dialog" v-show="visible">
...
<button #click="close">Close</button>
</div>
</template>
<script>
{
props: {visible: {type: Boolean, default: false}},
methods: {
close () {
// this.visible = false //It will get vue warn
}
}
}
</script>
So, how to close the dialog in my component, i can't update visible prop, i will get a error.
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "visible"
Props are one way data flow
You should not mutate the prop received from a parent component in the child component
So emit an custom event to mutate the prop in parent component itself
<template>
<div class="dialog" v-show="visible">
...
<button #click="close">Close</button>
</div>
</template>
<script>
{
props: {visible: {type: Boolean, default: false}},
methods: {
close () {
// this.visible = false //It will get vue warn
this.$emit('close-dialog')
}
}
}
</script>
parent component
<template>
<div>
<my-dialozg #close-dialog="visible = false" :visible="visible"><my-dialog>
</div>
</template>
Setup an event listener close-dialog on the dialog component and set the data property visible that you pass as a prop to false. You can do this inline as shown above or extract it into a method also
In vuejs, child component can not modify the parents property directly.
You can use events/event listeners for that. But since your example is simple you don't need an event for that.
Demo: https://jsfiddle.net/samayo/943bx5px/28/
Instead, you can pass the prop visible to your component as :visisble="visible" and watch the state change as:
watch: {
visible (val) {
if(!val) {
// close
}else{
open
}
}
}
Now, if visisble is toggled to false from the parent, your modal will not be visible.