vue transition-group appear event not being called - vuejs2

I am trying to hook into the appear event lifecycle and call a method in the component but the method getCalled() in my component is never called upon. The transition animation for after-leave event works as intended its just the appear events that do not.
according the documentation https://v2.vuejs.org/v2/api/#transition I can hook into the appear event system as demonstrated.
<transition-group name="order" tag="div" appear v-on:after-leave="activateScrollToBottom" v-on:after-appear="getCalled" v-on:before-appear="getCalled" v-on:appear="getCalled">

I had a similar situation and found out that I was missing the done callback in the appear method.
I put together a very simple CodePen to see, if it is actually working
https://codepen.io/rowild/pen/LJvEKm
JavaScript:
let app = new Vue({
el: '#app',
methods:{
// Transition hooks
beforeAppear(el){
console.log(' BEFORE APPEAR')
},
appear(el, done){
console.log(' APPEAR')
},
afterAppear(el){
console.log(' AFTER APPEAR')
}
}
},
// Lifecycle hooks
beforeCreate(){
console.log("BEFORE CREATE")
},
[...]
Template
<div id='app'>
<transition
appear
#before-appear="beforeAppear"
#appear="appear"
#after-appear="afterAppear"
#appear-cancelled="appearCancelled"
:css="false"
>
<p>TEST CONTENT</p>
</transition>
</div>
Console output
BEFORE CREATE
CREATED
BEFORE MOUNT
BEFORE APPEAR
APPEAR
AFTER APPEAR
MOUNTED
In case you found, what your problem was, would you mind to post an explanation and how you solved it? Thank you!

Related

How to bind a click event to a button when component is mounted vue js?

I am using a third party app (vue slick carousel) and I need to bind a click event to below button
<button type="button" data-role="none" class="slick-arrow slick-next" style="display: block;">
<svg...</svg>
</button>
So everytime I click this button a function gets triggered. This is what I tried in mounted and created life cycle, but didn't bind click event to it.
// console.log(document.querySelector(".slick-next")); // this returns element stated above
document.querySelector(".slick-next").addEventListener("click", () => {
console.log("Works");
});
I tried using setAttribute hoped it works, but it didn't work either.
Any help is appreciated. Thank you in advance.
I suspect they're probably using stopPropagation on the button click inside the component then.
There might be another way around this depending on your needs:
<VueSlickCarousel #beforeChange="checkChange">
...
</VueSlickCarousel>
methods: {
checkChange(oldSlideIndex, newSlideIndex) {
if (newSlideIndex > oldSlideIndex) {
console.log("Next button clicked");
}
}
}
Another option might be to use the <template #nextArrow="{ currentSlide, slideCount }"></template> slot inside the Carousel tag and use your own button. You'd probably have to implement your own logic for setting the next slide if you went this route.
You must to use the events handling of vuejs. Vuejs have many events for multiple cases.
For example in your case, you can use the event v-bind:click or simply #click. In documentation of vue slick, show this examples, wich is essentially the same.
I attach an code snippet:
<template>
<VueSlickCarousel ref="carousel">
<div><h3>1</h3></div>
/*...*/
</VueSlickCarousel>
<button #click="showNext">show me the next</button>
</template>
<script>
export default {
methods: {
showNext() {
this.$refs.carousel.next()
},
},
}
</script>

Which Vue handler is called?

If one function is bound to multiple Vue event hooks, is there any way to know which event triggered the function?
Here's a contrived example:
<div v-on:click="handler" v-on:mouseover="handler">
Which event called handler? Click or Mouseover?
</div>
This could also help understand the context for Transition hooks and determining which was triggered.
<transition appear v-on:appear="handler" v-on:enter="handler">
From inside handler, can we tell which hook was the caller?
A practical workaround for these situations would be decorating common functionality behind two simple wrapper functions, but it seems like Vue probably has a way of understanding its event calling context which might have been overlooked.
The event object's type will tell you which event triggered the handler. You can get the event object in Vue using the $event variable:
Vue.component('demo', {
template: '<div v-on:click="handler($event)" v-on:mouseover="handler($event)">Click or hover on me</div>',
methods: {
handler(e) {
console.log(e.type); // e is the event object passed in as $event
}
}
});
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<div id="app">
<demo></demo>
</div>

vue component is not passing events through

I have three nested components
<parent #event="handle">
<inbetween #event="$emit('event')">
<child> // emits event
</child>
</inbetween>
</parent>
so, when a child emits the event, I have to add that annoying part #event="$emit('event')" to the component inbetween, otherwise parent does not receive any event.
I hope it is not supposed to work that way. I wonder what could be wrong with it?
Yes thats how its supposed to work. Events go only from child to parent, it does not go from child up to grand parent. Therefore you have to emit the event from both child and inbetween component.
If you want to avoid this method you can use Event Bus:
https://medium.com/#andrejsabrickis/https-medium-com-andrejsabrickis-create-simple-eventbus-to-communicate-between-vue-js-components-cdc11cd59860
alligator.io/vuejs/global-event-bus
This is actually intentional. The reasoning is, when looking at the code for one component and you see that it's listening to an event, you can then look at the template to see where that event is coming from. If events could reach a component arbitrarily deep, it would be harder to figure out how and from where that event is being triggered.
However, Vue used to have a way of doing what you want to do, through the methods $broadcast and $dispatch, and they were eventually removed for the reasons talked about here. Here's a page from the docs which explains why, along with possible solutions, such as using a global event bus, or a centralized state management solution such as Vuex.
Vue custom events don't bubble.
The recommended way to handle ancestor/sibling communication in complex cases is using Vuex. If you have simple needs you can create Vue instance to use as event hub.
You would create a global variable:
var eventHub = new Vue(); // use a Vue instance as event hub
To emit events you would use in any component:
eventHub.$emit('myevent', 'some value');
And, to listen to that event, again, in any component, do:
eventHub.$on('myevent', (e) => {
console.log('myevent received', e)
});
Demo:
var eventHub = new Vue(); // use a Vue instance as event hub
Vue.component('parent', {
template: "#parent-tpl",
created() {
eventHub.$on('event', (e) => {
console.log('event received at parent! value:', e);
});
}
});
Vue.component('inbetween', {
template: "#inbetween-tpl"
});
Vue.component('child', {
template: "#child-tpl",
methods: {
emitEvent() {
eventHub.$emit('event', 123);
}
}
});
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<template id="parent-tpl">
<div>
<inbetween></inbetween>
</div>
</template>
<template id="inbetween-tpl">
<div>
<child></child>
</div>
</template>
<template id="child-tpl">
<div>
<h1>I'm the child</h1>
<button #click="emitEvent">Trigger EVENT</button>
</div>
</template>
<div id="app">
<p>{{ message }}</p>
<parent></parent>
</div>
Note: If creating a dedicated instance as event hub is something complicated in your environment, you can replace eventHub with this.$root (inside your components) and use your own Vue instance as hub.

Best way to a vue instance (app1) call another vue instance (app2)

Example:
<div id="app1"> <button #click="etc...">run APP1</button> ..</div>
<div id="app2">...
<button #click...>run APP1</button><!-- HERE! need a reference--> ...
<button #click...>run APP2</button>
</div>
How to say to Vue in APP2 that I need to call APP1?
I need that both buttons "run APP1" do exactly the same thing. Supposing that, in the illustration, the first is working, and the second is a copy/paste... But the second will not work because is into app2.
Concrete case: see "GOOD1" and "GOOD2" buttons at this code similar to the used in the last question...
Every Vue instance implements an events interface. This means that to communicate between two Vue instances (app1 and app2) you can use Custom Events.
So, in your example, before anything, give the first instance (that will emit events) a name so the other instance can reference it:
var app1 = new Vue({
el: '#app1'
})
And emit events from it (this code is at app1's template):
<button #click="$emit('go-modal', {header: 'HEL<big>LO2</big>', showModal: true})">GOOD1</button>
<button #click="$emit('go-modal', {header: 'BYE2', showModal: true})">GOOD2</button>
In the second instance (app2), listen to those events using $on:
new Vue({
el: '#app2',
// ...
mounted() {
app1.$on('go-modal', this.handleApp1);
},
methods: {
handleApp1(data) {
this.header = data.header;
this.showModal = data.showModal;
}
}
})
See demo JSFiddle here.

Can't stop Vue JS (v2) from reusing components

I have a Vue app that conditionally displays sets of the same component, I'd like to tie in to the created or mounted methods, but Vue keeps re-using earlier components and not triggering the methods. I've tried wrapping the components in keep-alive tags to no effect.
The code below shows what you would expect on the page: changing someVariable to 'test1' or 'test2' shows the relevant components, but the created/mounted methods only fire a maximum of 2 times (e.g. changing someVariable to 'test1' logs creation and mounting of 1 component with the label type1, then changing it to 'test2' only logs 1 more component with the label type3).
Currently using v2.1.10
HTML
<div id="app">
<div v-if="someVariable === 'test1'">
<keep-alive>
<test-component data-type="type1"></test-component>
</keep-alive>
</div>
<div v-if="someVariable === 'test2'">
<keep-alive>
<test-component data-type="type2"></test-component>
</keep-alive>
<keep-alive>
<test-component data-type="type3"></test-component>
</keep-alive>
</div>
</div>
JS/Vue
Vue.component('test-component', {
props: ['data-type'],
template: '<div><p>In the component: {{ dataType }}</p></div>',
created: function () {
console.log('component created: ', this.dataType);
},
mounted: function () {
console.log('component mounted: ', this.dataType);
}
});
var app = new Vue({
el: '#app',
data: {
someVariable: ""
}
});
You should use a watcher on your someVariable instead of trying to hook on created and mounted hooks.
Components are created, and mounted the first time they are visible (rendered). There are NO "shown" or "hidden" hooks.
See https://v2.vuejs.org/v2/guide/computed.html#Watchers:
watch: {
someVariable: function (newValue) {
// test newValue and do what you have to do!
}
}
For your specific example removing keep-alive from the second if should do the trick https://jsfiddle.net/z11fe07p/464/
An interesting thing is that vue seems to re-use the previously rendered component. So if you have one component in the first if when switching to the next if with 2 components it will re-use one component and create a new one for the second component from the block. When getting back to the first if block it will re-use one of the 2 already rendered components.
As mentioned above, a watcher is more suited for such cases, thus getting you rid of handling logic in places where you don't have full control. You can see this tip right here in the docs https://v2.vuejs.org/v2/api/#updated