Vue2 component $emit not calling parent function - vue.js

I have a button inside of a component that is calling an $emit('close') and the container that is holding the component has a #close='myMethod' and myMethod does not get called when you click the button that's inside the component.
Html:
<button #click="myMethod">outer</button>
<div class="parent" #close="myMethod">
<my-component></my-component>
</div>
<div id="my-component" style="display:none;">
<button #click="$emit('close')">emit</button>
</div>
JS:
Vue.component('my-component', {
template: '#my-component'
});
var app = new Vue({
el: '#app',
methods: {
myMethod: function() {
console.log('myMethod invoked');
}
}
});
If you click the outer button, the method gets invoked, but not the button inside the template. What am I missing?
Jsbin: http://jsbin.com/cuhipekocu/edit?html,js,console,output

You're not listening to the event on the component. Try
<div class="parent">
<my-component #close="myMethod"></my-component>
</div>

Related

How to get parent component's ref in child component

I want to pass button element to the child component using refs.
<button ref="button">
Button
</button>
<child :element="$refs.button" />
But in child component element prop comes null. How can I send button element to the child component ?
As per my understanding, You want to use the button element in multiple components. You can make button itself as a separate component and use it where ever you want.
You can try something like this :
Vue.component('customBtn', {
props: ['buttontext'],
template: '<button>{{ buttontext }}</button>'
});
Vue.component('child', {
template: '<custom-btn buttontext="This is a child button"></custom-btn>'
});
var app = new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<custom-btn buttontext="This is a parent button">
</custom-btn>
<child></child>
</div>

Vue.js : How can I do native events handling of child component from parent component

I have a component that wraps an anchor:
Vue.component('wrapper-link', {
template : `
<div>
text link
<div>
`
});
I'm using it like this in my app:
template:
<div id="app">
<wrapper-link #click.stop="onClickEvent"></wrapper-link>
</div>
script:
let app = new Vue({
el: '#app',
methods: {
onClickEvent() {
console.log('clicked');
}
}
})
I was expecting that after clicking text link, the native click-event would be blocked and the console would log 'clicked'; but none of that happened. The native click event worked (navigation occurred).
I know of event.preventDefault(), but I want to use Vue's event modifiers.
You used the .stop event modifier (calls event.stopImmediatePropagation()), but the behavior you're seeking is accomplished with .prevent (calls event.preventDefault()):
<wrapper-link #click.prevent="onClickEvent" />
Vue.component('wrapper-link', {
template: `
<div>
<a href="http://google.com"
target="_blank"
v-on="$listeners">Google</a>
</div>
`
});
new Vue({
el: '#app',
methods: {
onClick(e) {
console.log('click');
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<wrapper-link #click.prevent="onClick" />
</div>

how to substitute named slot from parent to child to grandchild with Vuejs

I am faced with a situation to render a third party date picker and also get the value from the third party component. since it is a third party i can't define a prop on it. However, the third party component provides an ability to pass variable in v-model.
So the design of my components requires me to be able to pass named slot from parent to child to grandchild.
for example;
//============ enter-or-upload-task.js =============
Vue.config.productionTip = false;
const sourceoftruth = {orderdate:''};
Vue.component('upload-by-csv',{
template:
`<div><input type="file" /></div>
`
});
//here i want to use the date picker
Vue.component('enter-task-by-form',{
data:function(){
return {
sourceoftruth
};
},
methods:{
getOrderDate: function(){
console.log(sourceoftruth.orderdate);
}
},
template:
`<div>
<label>Order Date:</label>
<!--Hoping to get datepicker component displayed here -->
<slot name="picker"></slot>
<input type="submit" #click.prevent = "getOrderDate()" />
</div>
`
});
const EnterTaskOrUploadByCSV = {
template:
`<div>
<upload-by-csv></upload-by-csv>
<enter-task-by-form>
<!-- wish to pass this named slot to this component -->
<slot name="picker"></slot>
</enter-task-by-form>
</div>
`
}
new Vue({
el:"#app",
data:{
sourceoftruth
},
components:{
'datepicker':vuejsDatepicker,
'enter-form-or-csv':EnterTaskOrUploadByCSV
}
})
// ===========================================================
The index.html is
<script src="./js/lib/vue.js></script>
<script src="./js/lib/vuejs-datepicker.js"></script>
<div id="app">
<enter-form-or-csv>
<datepicker v-model="sourceoftruth.orderdate" slot="picker">
</datepicker>
</enter-form-or-csv>
</div>
<script src = "./js/components/enter-or-upload-task.js"></script>
I have tried passing the named slot to the appropriate position but couldn't get it working. Please I need help on how to solve this.
Glad it's working now. The changes I made that it to work
in EnterTaskOrUploadByCSV i added a template slot to hold the datepicker component going to be received from the parent. then the grandchild component would be expecting the template named slot.
const EnterTaskOrUploadByCSV = {
template:
`<div>
<upload-by-csv></upload-by-csv>
<enter-task-by-form>
<template slot="passpicker">
<slot name="picker"></slot>
</template>
</enter-task-by-form>
</div>
`
}
The grandchild component expecting the template named slot.
Vue.component('enter-task-by-form',{
data:function(){
return {
sourceoftruth
};
},
methods:{
getOrderDate: function(){
console.log(sourceoftruth.orderdate);
}
},
template:
`<div>
<label>Order Date:</label>
<!-- Datepicker will show now -->
<slot name="passpicker"></slot>
<input type="submit" #click.prevent = "getOrderDate()" />
</div>
`
});

How to add/remove class on body tag when open/close modal in vuejs

I have a modal in one of my pages and I want to add a class “active” on body when I open the modal, so I can make the body overflow hidden (no scroll).
Is there a way to toogle a class on the body tag when I click from one component? I can't figure it out...
I use routes
<template>
<div id="app">
<Header />
<router-view/>
<Footer />
</div>
</template>
Thx in advance
The correct way of doing this in Vue is to communicate between components, in this case it might not be a simple parent/child communication, so you might want to create an Event Bus.
By using this approach the modal's code is has minimum effects on the rest of your application, it only dispatches events that you can subscribe to from any other component.
Note: In this case you won't add the class on your body tag (because you can't mount Vue on body), but you may just add it to your root div to have a similar result.
const eventBus = new Vue();
Vue.component('modal', {
props: ['isOpen'],
template: `
<div class="modal" v-if="isOpen">This is a modal</div>
`,
});
Vue.component('wrapper', {
template: `
<div>
<modal :isOpen="isModalOpen"></modal>
<button #click="toggleModal">toggle modal</button>
</div>
`,
data() {
return {
isModalOpen: false,
}
},
methods: {
toggleModal() {
this.isModalOpen = !this.isModalOpen;
eventBus.$emit('toggleModal', this.isModalOpen);
}
}
});
new Vue({
el: "#app",
data: {
active: false,
},
created() {
eventBus.$on('toggleModal', (isModalOpen) => {
this.active = isModalOpen;
});
},
})
.active {
background: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app" :class="{active}">
<wrapper></wrapper>
</div>
This should help
document.body.className += 'active'

Vue.js: Property or method is not defined on the instance

I have a simple vue component:
Vue.component('repo-button', {
props:["check_in_id", "repo_id"],
template: '#repo-button',
methods: {
fetchRepo: function() {
console.log("this is working")
}
},
data: function() {
var that = this;
return {}
}
});
var repositionings = new Vue({
el: "#repo-vue"
})
And my view looks like:
<script type="text/x-template" id="repo-button">
<div class='socialCircle-item success'>
<i class='fa fa-comment'
:data-check_in='check_in_id'
:data-repo='repo_id'>
</i>
</div>
</script>
<div id="repo-vue">
<div is="repo-button" repo_id="8" check_in_id="30" #click="fetchRepo></div>
</div>
and I see the error:
[Vue warn]: Property or method "fetchRepo" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
It seems like I have registered the method, but it's looking in the root component for the method when I think it should be an instance method.
How am I supposed to include the fetchRepo instance method?
You've defined fetchRepo as a method of your component, but you are trying to call it from outside the component.
Move the click handler inside your component.
<script type="text/x-template" id="repo-button">
<div class='socialCircle-item success' #click="fetchRepo">
<i class='fa fa-comment'
:data-check_in='check_in_id'
:data-repo='repo_id'>
</i>
</div>
</script>
<div id="repo-vue">
<div is="repo-button" repo_id="8" check_in_id="30"></div>
</div>