Why Vue Router doesn't run javascript transition hooks sync? - vue.js

I am new in vue router and vue.js. I just try to use javascript transitions on vue-router. Why beforeLeave event doesn't fire before route change ? But it is working as expected with css transitions. it works great with css transitions. But it doesn't work with javascript transitions... Here is jsfiddle example...enter method works as expected. but beforeLeave method doesn't work as expected and has no transition chain between routes. Please help..
jsfiddle
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenMax.min.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link to="/foo">/foo</router-link>
<router-view></router-view>
</div>
and this is js
const Home = { template: '<transition v-on:enter="enter" v-on:leave="leave" ><div>Home</div></transition>',
methods:{
enter:function(el, done){
TweenMax.from(el,2,{x:-60,onComplete:done});
},
leave:function(el, done){
TweenMax.to(el,2,{x:60,onComplete:done});
}
}
}
const Foo = { template: '<transition v-on:enter="enter" v-on:leave="leave" ><div>Foo</div></transition>',
methods:{
enter:function(el, done){
TweenMax.from(el,2,{x:-60,onComplete:done});
},
leave:function(el, done){
TweenMax.to(el,2,{x:60,onComplete:done});
}
}
}
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/foo', component: Foo }
]
})
new Vue({
router,
el: '#app',
data: {
msg: 'Hello World'
}
})

I have solved it. Do not use multiple transitions for chaining. if you use css transition it is not problem. But if you use javascript transition it will not run as expected. So use transition on parent element which is "router-view". if you want to change transition for each route, just check route.name with an if block and make a transition for specific route.
here is jsfiddle
const Home = { template: '<div>Home</div>',
methods:{
enter:function(el, done){
TweenMax.from(el,2,{x:-60,onComplete:done});
},
leave:function(el, done){
TweenMax.to(el,2,{x:60,onComplete:done});
}
}
}
const Foo = { template: '<div>Foo</div>',
methods:{
enter:function(el, done){
TweenMax.from(el,2,{x:-60,onComplete:done});
},
leave:function(el, done){
TweenMax.to(el,2,{x:60,onComplete:done});
}
}
}
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/foo', component: Foo }
]
})
new Vue({
router,
el: '#app',
data: {
msg: 'Hello World'
},
methods:{
enter:function(el, done){
TweenMax.from(el,2,{x:-60,onComplete:done});
},
leave:function(el, done){
TweenMax.to(el,2,{x:60,onComplete:done});
}
}
})

Related

Vue - Keep default Router-View alive when change another named view

Situation:
I use, beside of the default route-view, a named route-view. I want to keep the DEFAULT route-view alive when I call the ArticleComponent, but as you can see, you can call the ArticleComponent from 2 different routes/components. You can find a fiddle link under the code snippet.
What I want to do:
If I open the ArticleComponent from ListingComponent, then ListingComponent should stay alive in the default route-view.
If I call the ArticleComponent from the FeedComponent, then the FeedComponent should stay alive in the default route-view.
My code:
const HomeComponent = {
template: '<h4>Home</h4>'
};
const FeedComponent = {
template: `<div>
<h4>FeedComponent</h4>
<router-link to="/article/1">Article 1</router-link> -
<router-link to="/article/2">Article 2</router-link>
</div>`
};
const ListingComponent = {
template: `<div>
<h4>ListingComponent</h4>
<router-link to="/article/1">Article 1</router-link> -
<router-link to="/article/2">Article 2</router-link> -
<router-link to="/article/3">Article 3</router-link>
</div>`
};
const ArticleComponent = {
template: `<h4>Article {{ $route.params.id }}</h4>`
};
const routes = [
{
path: '/',
component: HomeComponent
},
{
path: '/feed',
component: FeedComponent
},
{
path: '/listing',
component: ListingComponent
},
{
path: '/article/:id?',
components: {
default: FeedComponent, // <--- dynamically
secondary: ArticleComponent
}
}
];
const router = new VueRouter({
routes
});
new Vue({
el: '#app',
router
});
Fiddle:
https://jsfiddle.net/kvnvooo/b589uvLt/9/
You can use Navigation guards to alter default component dynamically...
{
path: '/article/:id?',
components: {
default: FeedComponent,
secondary: ArticleComponent
},
beforeEnter: (to, from, next) => {
if(from.fullPath === '/listing') {
to.matched[0].components.default = ListingComponent
} else if(from.fullPath === '/feed') {
to.matched[0].components.default = FeedComponent
}
next();
}
}
https://jsfiddle.net/dhmLby6f/7/

VueJS dynamic routes and components

Using cue-cli 3. Is it possible to do this (router.js):
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: e.template,
},
]);
});
});
e.template is a string 'Default' and of course VueJS says:
route config "component" for path: /privacy-policy cannot be a string id. Use an actual component instead. Tried with Vue.component(e.template) no luck.
What I want to do here is create dynamic routes based on XHR response.
Here is all router.js code:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Default from './views/Default.vue'
import Test from './views/Test.vue'
import axios from "axios";
Vue.use(Router);
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
]
});
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: e.template,
},
]);
});
});
export default router;
Currently I ended up with this solution:
function getComponent(name) {
let component = null;
switch(name)
{
case 'Default':
component = Default;
break;
case 'Test':
component = Test;
break;
}
return component;
}
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: getComponent(e.template),
},
]);
});
});
Another one more cleaner solution:
const components = { Default, Test }
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: components[e.template],
},
]);
});
});
If e.template stores the template string,
You should wrap it as one options object like {template: e.template, props: {}, data: function () {} }, then call Vue.extend to construct the component.
or you can ignore Vue.extend because Vue will call Vue.extend to construct the component automatically.
Check the usage at Vue Guide: Vue.component
Edit as the OP states e.tempate is one component name:
if e.template is the name of component, uses Vue.component(e.template).
Vue.config.productionTip = false
const router = new VueRouter({
routes: [
]
})
Vue.component('test', {
template: '<div>I am Predefined component -> {{index}}</div>',
props: ['index']
})
let routerIndex = 1
setInterval(()=> {
let newComponent = routerIndex%2 ? {template: '<div>I am User -> {{index}}</div>', props: ['index']} : Vue.component('test')
router.addRoutes([{
path: '/Test' + routerIndex,
name: 'Test' + routerIndex,
component: newComponent,
props: { index: routerIndex }
}])
console.log('add route = ', '/Test' + routerIndex, ' by ', routerIndex%2 ? 'options object' : 'Vue.component')
routerIndex++
}, 2000)
Vue.use(VueRouter)
app = new Vue({
el: "#app",
router,
data: {
routeIndex: 0
},
watch: {
routeIndex: function (newVal) {
this.$router.push({'name': 'Test'+newVal})
}
}
})
div.as-console-wrapper {
height: 100px;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<p>Current Route Index: {{routeIndex}}</p>
Test Route: <input v-model="routeIndex" type="number">
<router-view></router-view>
</div>

Vue router - stay in the same url(without redirect)

I am using this simple-router example, but with my own components:
$( document ).ready(function() {
const Home = { template: '<div id="login"><login-comp></login-comp></div>' };
const Foo = { template: '<div>Foo</div>' };
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/foo', component: Foo }
]
})
new Vue({
router,
el: '#router',
data: {
msg: 'Hello World'
}
})
});
It works fine but as a change from one route to another the page redirect to my base url, for example my site is:
https://example.com/vue/index.jsp
after changing to /foo, the url changes to
https://example.com/foo
My index.jsp router:
<div id="router">
<router-link to="/">/home</router-link>
<router-link to="/foo">/foo</router-link>
<router-view></router-view>
</div>
Thank's for any help.
That is how it's meant to work, when you set mode to history:
mode: 'history'
Remove that part, and you'll get a hash after your url, and it will work properly.

Vue lifecycle events triggers on every route

According to the documentation https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram all events from beforeCreate till mounted should be called once. But they are being triggered on every vue-router path navigated
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App },
created: function () {
this.$http.get('/user/get').then(response => {
if (response.data.error) {
console.log(response.data.error)
} else {
User.set(response.data.user)
router.go('/dashboard') // this does force looping
}
}, response => {
router.go('/')
})
}
})
This is App.vue
<template>
<div id="app">
<topmenu></topmenu>
<router-view></router-view>
</div>
</template>
<script>
import Topmenu from '#/components/topmenu'
export default {
name: 'app',
components: {
topmenu: Topmenu
}
}
</script>
The Router.vue
Vue.use(Router)
let route = new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/user/signup',
name: 'Signup',
component: Signup
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard
}
]
})
route.beforeEach((to, from, next) => {
if (to.path.match(/^(\/|\/signup)$/)) {
return next()
}
if (User.valid()) {
return next()
}
route.push('/')
})
export default route
How to avoid this events from being triggered on every route switched?

How to call function from router template?

I am doing very simple page with url's switching. In one of url I need to call #click methods. Here is my code:
const NotFound = { template: '<p>Page not found</p>' }
const Home = { template: '<p>home page</p>' }
const Faq = { template: '<p>Faq page</p>' }
const Book = { template: `
<div>
<button #click="foo()">test</button>
</div>
` }
const routes = [
{path: '/', component: Home},
{path: '/book', component: Book},
{path: '/faq', component: Faq}
]
const router = new VueRouter({
routes // short for `routes: routes`
})
new Vue({
el: '#app',
router,
data: {
},
methods:
{
foo()
{
console.log("ffffff");
}
}
})
But I am getting error: Property or method "foo" is not defined on the instance but referenced during render.
Because you're trying to define child component(book)'s method in parent component. Please just move methods property from parent component to child component.
Here's a sample.
const NotFound = { template: '<p>Page not found</p>' }
const Home = { template: '<p>home page</p>' }
const Faq = { template: '<p>Faq page</p>' }
const Book = { template: `
<div>
<button #click="foo()">test</button>
</div>
`
, methods: // <-- here you go
{
foo()
{
console.log("ffffff");
}
}}
const routes = [
{path: '/', component: Home},
{path: '/book', component: Book},
{path: '/faq', component: Faq}
]
const router = new VueRouter({
routes // short for `routes: routes`
})
Vue.use(VueRouter);
new Vue({
el: '#app',
router,
data: {
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.7.0/vue-router.min.js"></script>
<div id="app">
<router-link :to="{ path: '/' }">Home</router-link>
<router-link :to="{ path: '/faq' }">FAQ</router-link>
<router-link :to="{ path: '/book' }">book</router-link>
<router-view></router-view>
</div>