How can I execute animations upon navigating (via $router) to and from a page/url. Ie, how can I perform animations on show and on leave of a page? I will have different enter and leave animations for each page. The animations are complex thus we use javascript callbacks instead of css3 implementation
I have attempted the following but the transition callbacks dont fire every time. For some pages the callbacks fire for others they dont and they all have the exact code. It's quite weird.
Example MyPage.vue (could be Contact.vue, AboutUs.vue etc.):
<template>
<transition v-on:enter="enter" v-on:leave="leave">
<main>
<p>Foo</p>
</main>
</transition>
</template>
<script type="text/javascript">
import gsap from 'gsap' // include animation library
export default {
methods: {
enter: function (el, done) {
// I only see the text enter sometimes for a page. Its not consistently firing
console.log('enter')
done()
},
leave: function (el, done) {
// I only see the text leave sometimes for a page. Its not consistently firing
console.log('leave')
done()
}
}
}
</script>
Check this example : https://codesandbox.io/s/n38vz0pnzp
It's triggering as expected.
I added the appear prop which triggers the enter animation even for the first component mounting (when starting the app).
Related
I have a Vue (2.6.12) router setup with a standard nav menu on the left side of the page. When I change the route, the current main page content transitions out (with a fade-slide) and the new route component transitions in (for what it's worth, I'm also using the Vuetify UI library, but it shouldn't have any bearing on the problem).
Is there a callback function that can run after the transition of the incoming component has completed? The mounted lifecycle hook of the component appears to run before or at the same time as the incoming component is beginning to animate (which makes sense since the component is obviously built and available at that stage). I would like to run some code after the transition has completed. For example, my main component where the routes are handled looks like:
<v-main>
<transition name="content-toggle" mode="out-in">
<router-view />
</transition>
</v-main>
I've tried adding v-on:after-enter="doIt"into my transition above, and then adding the following to my main component:
methods: {
doIt(el) {
console.log('doing it...', el);
},
},
...and that does fire when I want it to, at the end of the incoming transition, but I need the hook to be defined in the incoming component itself. Is it possible to accomplish that?
I pretty new to vuejs and am building a vuejs project. One of the tasks I am stuck at is, that I want to call a function written in the javascript part of vuejs from the html part of vuejs without creating any buttons or textboxes. I want to call this function as soon as my app starts. How do I achieve this? When I use mounted (vue lifecycle hook), the page it redirects to keeps refreshing. would appreciate some leads on this.
For example, I have a code:
<template>
<h1>Hello World!</h1>
<!--I WANT TO CALL THE auth0login function here. How do I do that without creating a button/text field,etc -->
</template>
<script>
export default {
data: () => ({
return : {
clientID: process.env.example
}
}),
methods:{
auth0login(){
console.log('logging in via Auth0!');
Store.dispatch('login');
}
}
}
</script>
I want to call the auth0login function defined in the script part in the html part above in order to execute the functionalities of the auth0login function. I want to do this without creating any buttons in the html file and simply just execute the auth0login function when the app launches.
How do I do this?
You need to call auth0login() only when you're not already logged I guess.
I think the reason is after user already logged in, they go back to the initial page, and the function in mounted() hook run again. Thats why they keep get redirect to login page. This not happen when you insert the button, because the button require user to click at it.
To fix this, you need to define a vuex state to store whether user has logged in or not, and in mounted() hook, just called it when they not login yet.
So. Instead of
mounted: function() { console.log('logging in via Auth0!'); this.auth0login() } }
Add a check login state
mounted() {
if(!this.$store.state.userLoggin) // here i define userLoggin and vuex state, you should update it to true whenever user successfully login
{ console.log('logging in via Auth0!'); this.auth0login() //only call it when user not loggin
}
}
And remember to update your vuex state.userLoggin when user successfully login in the auth0login() function
Currently I create a html5 video player component for an universal nuxtjs/vuejs app.
There is an autoplay attribute for the video tag to start videos after navigate to them.
Usually browsers don't do that directly after page load, it's prohibited.
I need a variable in my component to know if autoplay will be possible to style elements based on this information.
In other words: The variable should be true if the current page was only rendered on client-side, but false if it was rendered on server-side first.
It's not possible to work with "window.history.length" because the autoplay will also not be possible after refresh, although this would not have an effect on the history length.
Also it's not possible to set a variable in the "created" method since it will be called on server- and client-side too.
In nuxt - there is the <client-only> component that renders components only on the client side
<template>
<div>
<sidebar />
<client-only placeholder="Loading...">
<!-- this component will only be rendered on client-side -->
<comments />
</client-only>
</div>
</template>
In nuxt, there are is asyncData which is run beforeCreate(), i.e. you can retrieve data from the server side in asyncData before the component is mounted.
Alternatively, you can check on the created() for the process, e.g.
created() {
if (process.client) {
// handle client side
}
}
edit
https://stackoverflow.com/a/53058870/2312051
Audio.play() returns a Promise which is resolved when playback has been successfully started. Failure to begin playback for any reason, such as permission issues, result in the promise being rejected.
const playedPromise = video.play();
if (playedPromise) {
playedPromise.catch((e) => {
if (e.name === 'NotAllowedError' ||
e.name === 'NotSupportedError') {
//console.log(e.name);
}
});
}
In your case, looks like your browser/os does not allow automatic playing of audio. The user agent (browser) or operating system doesn't allow playback of media in the current context or situation. This may happen, for example, if the browser requires the user to explicitly start media playback by clicking a "play" button. Here is the reference.
One possible solution would be to get the length of the Vue router entries. This seems currently not to be possible without manually tracking. So I ended with this code in my layout component:
watch: {
$route () {
Vue.prototype.$navigated = true
}
}
The value of $navigated is undefined or true and so I know in the layout children components if video autoplay is possible or not.
I have a some middleware in my Nuxt app that closes a fullscreen mobile menu when a new route is clicked. What I am experiencing is the following:
user clicks nuxt-link
menu closes
asyncData delay (still shows current page)
new page is loaded upon resolved asyncData
What I would like is the following:
user clicks nuxt-link
asyncData delay (if exists)
menu closes upon new page load
Is it possible to have an asyncData watcher in Nuxt middleware?
I know I can hack this by creating a store value that tracks asyncData load, but I'd rather not have to wire up such a messy solution to each and every page that uses asyncData.
Note - not every page uses asyncData.
I think that the best option would be to leave the menu open, and then when the new page finishes loading (probably on the mounted hook) send an event or action to close the menu.
I figured this out in a more elegant solution than having to implement a store.dispatch function on each individual asyncData function.
So what I did was use a custom loading component: https://nuxtjs.org/api/configuration-loading/#using-a-custom-loading-component
However, instead of showing a progress bar or any sort of loading animation, I just used this component to set a loading state in my vuex store. Note - it forces you to have a template, but I just permanently disabled it.
<template>
<div v-if="false"></div>
</template>
<script>
export default {
methods: {
start() {
this.$store.dispatch('updateLoadingStatus', true);
},
finish() {
this.$store.dispatch('updateLoadingStatus', false);
}
}
};
</script>
Then in my middleware, I set up an interval watcher to check when loading has stopped. When stopped, I stop the interval and close the mobile menu.
export default function({ store }) {
if (store.state.page.mobileNav) {
if (store.state.page.loading) {
var watcher = setInterval(() => {
if (!store.state.page.loading) {
store.dispatch('updateMobileNav', false);
clearInterval(watcher);
}
}, 100);
} else {
store.dispatch('updateMobileNav', false);
}
}
}
This doesn't specifically have to be for a mobile menu open/close. It can be used for anything in middleware that needs to wait for asyncData to resolve. Hope this can help anyone in the future!
Without reloading the whole page I need to reload the current route again (Only a component reload) in a vue app.
I am having a path in vue router like below,
{
path: "/dashboard",
name: "dashboard",
component: loadView("Dashboard"),
},
When user clicks on the Dashboard navigation item user will be redirected to the Dashboard page with vue router programmatic navigation
this.$router.push({ name: "dashboard" });
But when user already in the dashboard route and user clicks the Dashboard nav item again nothing happens. I think this is vue router's default behaviour. But I need to force reload the Dashboard component (Not to refresh the whole page).
I can't use beforeRouteUpdate since the router is not updated. Also I have tried the global before guards like beforeEach. But it is also not working.
How can I force reload the dashboard component without reloading the whole page?
It can be done in two ways.
1) Try doing vm.$forceUpdate(); as suggested here.
2) You can take the strategy of assigning keys to children, but whenever you want to re-render a component, you just update the key.
<template>
<component-to-re-render :key="componentKey" />
</template>
<script>
export default {
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
}
</script>
Every time that forceRerender is called, the prop componentKey will change. When this happens, Vue will know that it has to destroy the component and create a new one.
What you get is a child component that will re-initialize itself and “reset” its state.
Not mentioned here, but as the offered solutions require a lot of additional work just to get the app to render correctly, which imo is a brittle solution.. we have just implemented another solution which works quite well..
Although it is a total hack.
if (this.$route.name === redirect.name) {
// this is a filthy hack - the vue router will not reload the current page and then have vue update the view.
// This hack routes to a generic page, then after this has happened the real redirect can happen
// It happens on most devices too fast to be noticed by the human eye, and in addition does not do a window
// redirect which breaks the mobile apps.
await this.$router.push({
name: RouteNames.ROUTE_REDIRECT_PLACEHOLDER
});
}
... now continue to do your normal redirect.
Essentially, redirect to a placeholder, await the response but then immediately continue to another page you actually wanted to move toward