Vue.js: how to use the afterEnter hook with an async component - vue.js

I would like to use JS Hook as described here. Specially, I want to use the afterEnter hook with an async component.
This is my async component:
Vue.component('example', function(resolve, reject){
let data = {
text: 'test data',
};
$.post('http://example.com', data, function(r){
r = JSON.parse(r);
if( r.success ) {
resolve({
template: r.data,
afterEnter: function(el, done){
console.log('test');
}
});
}
});
});
This is what the ajax call gets from the server, and it's what is passed to the template in r.data.
<transition v-on:after-enter="afterEnter"></transition>
These are the two errors that I get.
[Vue warn]: Property or method "afterEnter" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
[Vue warn]: Invalid handler for event "after-enter": got undefined
Is it possible to use JS hooks with async components? And if not, how should I approach this? My objective is to run custom JS after Vue (and/or vue-router) inject the component template onto the page, so that I can initiliaze image sliders and whatnot. It is important that my custom JS fires every time the component is navigated to, and not only on the first load.
Thank you.

That warning means that Vue is looking for (but unable to find) a property or method named "afterEnter", which you reference in your template. You have defined afterEnter in your resolve function as if it is a lifecycle hook, but it needs to be one of your Vue instance's methods.
So, your resolve function should look like this:
resolve({
template: r.data,
methods: {
afterEnter: function(el, done) {
console.log('test');
}
}
});

Related

How can I send and get with this.$router.push the params between two components

I have two components.
The first one:
This component has this one:
this.$router.push({path: '/dte', params: { id: 1 }});
I am sending and redirecting to /dte
And I have a second one which it has in the created part of the vuejs code this:
export default {
created() {
alert(this.$router.params.id);
}
}
I wonder:
How can I check if id is defined?
I wonder why does it display this error - Error in created hook: "TypeError: Cannot read property 'id' of undefined"?
Thanks
Be careful, there are two globals objects injected by vue-router:
$router: The vue-router instance
$route: The current route state described in an object
Here, you have to check for this.$route.params.id to get the current route parameter.

Does Vue automatically remove all vue/vuex watchers on component destroy?

If the component subscribes to the Vuex events like:
this.$store.watch or this.$store.subscribe
Is it necessary to remove the watcher during component destroy or Vue already takes care of it under the hood?
P.S: Current logic runs on 2.6.10 Vue version.
Following #Cristiano Soleti I checked if listeners are still being called after component that registered them was destroyed.
...and at least in 2.6.10 version, Vuex watcher are actually not removed automatically when component is destroyed. And thus should be unregistered explicitly by developer in beforeDestroy lifecycle hook.
The VueX docs are vague, however it does say "To stop watching, call the returned unwatch function." with no other information, which implies that watchers never get removed any other way: https://vuex.vuejs.org/api/#watch
So, you should manually remove a VueX watcher when destroying a component.
Here's an example:
MyComponent.vue
data: function() {
return {
myWatcher: null // Holds the watcher, so we can reference it
}
},
mounted() {
// Invoke the watcher
this.myWatcher = this.$store.watch(state => state.myStoreVariable, (valueNew, valueOld) => {
// Do stuff
console.log("valueOld: " + valueOld + ", valueNew: " + valueNew)
})
},
beforeDestroy () {
this.myWatcher() // Simply calling the watch holder as a function (ie with parentheses) will remove the watcher.
},

How to write a single piece of code running both on route A (mounted hook) and also when arriving at route A?

Currently in order for me to do a thing X either when loading route A or arriving to route A, I need to write X twice:
watch: {
$route (to, from){
if (to.name === 'simulation-step-3-sequence') {
EventBus.$emit('actionIsSortable');
}
}
},
async mounted () {
if (this.$route.name === 'simulation-step-3-sequence') {
EventBus.$emit('actionIsSortable');
}
}
Is there a way to simplify this so I write X (the emit line) only once?
This technique is really needed only in two cases - multiple routes are using same component or dynamic routes (with parameters) ...see the docs
In this cases when the new route is using same component as the old route, Vue Router will just reuse existing component instance.
You can place a key on router-view and disable this behavior. Your site will be less effective but you can get rid of $route watcher
<router-view :key="$route.fullPath" />
Other option is to change watcher definition like this:
watch: {
$route: {
immediate: true,
handler: function(to, from) {
console.log(`Route changing from '${from}' to '${to}'`);
}
}
}
Vue will call watcher handler on route changes but also when the component is created (so you can remove the code in lifecycle hook)

How do I stop my vue component from rendering until a call completes

I've googled this but I can't find any specific solution. Basically I have a vue component that depends on an init call and I want it to stop rendering until the call completes, at which point I want the component to render. Seems simple but unless I'm missing something I can't find any lifecycle method that does that.
You can use v-if for that purpose
<template>
<div v-if="loaded"></div>
</template>
<script>
export default {
name: 'TestComponent',
data: function () {
return {
loaded: false
}
},
created() {
callExternalServices().then(() => {
this.loaded = true
})
}
}
</script>
It will render an empty component until loaded == true
Basically you make an init call in the created or mounted lifecycle method and you initialize a data object with the response of the call. If you don't change data during the call there is no reason for vue to render anything.
Maybe you can provide a jsfiddle that show exactly your problem.

Vue: same route same component, but different params

I'm creating a SPA where after each HTTP response, the app goes to the same component with the same route, but the props passed to the component will be different based on the response. I understand the reuse feature in Vue and it won't reload. But I need the newly returned data every time I trigger the router to go to the component. My app.vue:
upload(formData).then(x => {
router.push({
name: 'Chart',
params: {
chartData: x
}
});
});
Then the target Chart.vue:
props: ['chartData'],
data: function() {
// do some processing using props
return {...}
}
My problem is that since for my router, the route doesn't change, hence other approaches like using :key=$route.fullPath or the beforeRouteUpdate don't work. Then how can I make the Chart component recompute the data? I've checked thru computed() and watch as well but it's not working.