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

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/

Related

How to open this route in new window?

if (success) {
context.bookingData.city = context.city;
context.bookingData.departureFlightDate = context.departureFlightDate;
context.bookingData.arrivalFlightDate = context.arrivalFlightDate;
context.bookingData.idParkingService = context.idParkingService;
context.bookingData.Price = context.bookings.price.items.data.price;
//const booking = JSON.stringify(context.bookingData);
context.addBookingData(context.bookingData);
context.$router.push({
path: "parkplatz-buchen-schritt-2",
});
}
how to open this path in new window or tab at the same time I have to pass context data to the destination
To open the route in a new window
Home.vue
<template>
<button v-on:click="openInNewWindow()">Open in new window</button>
</template>
<script>
export default {
methods: {
openInNewWindow(){
let routeData = this.$router.resolve({
path: '/about',
query: {
city : 'test',
departureFlightDate : 'test',
arrivalFlightDate : 'test',
idParkingService : 'test',
price : 'test'
}
});
window.open(routeData.href, '_blank');
}
}
}
</script>
Router
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
About.vue
<template>
<div class="about">
<h1>This is an about page</h1>
{{this.$route.query.city}}
</div>
</template>
<script>
export default {
mounted(){
console.log(this.$route.query)
}
}
</script>

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.js go back button browser detail back to lister position

I have a test lister products and detail. Now when i go back with de backbutton in the browser is doesn't go back to the wright position.
So if i scroll down in the lister , click product to detail and back it doesn't go to the position i clicked before.
How can i achieve that?
Main.js
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
Router:
export default new Router({
mode: 'history',
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
console.log(savedPosition)
return savedPosition;
} else {
return {x: 0, y: 0};
}
},
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/products',
name: 'Products',
component: Products,
props: true,
},
{
path: '/product/:id',
name: 'Productdetail',
component: Productdetail,
props: true
}
]
})
Lister:
<template>
<ul v-if="posts && posts.length">
<li v-for="post of posts" v-bind:id="post.id">
<p><strong>{{post.title}}</strong></p>
<p>{{post.body}}</p>
<router-link :to="{path: '/product/'+post.id, replace: true}">{{post.title}}</router-link>
<hr>
</li>
</ul>
</template>
import axios from 'axios';
export default {
name: 'Products',
props: ["guid"],
data() {
return {
posts: [],
msg: 'Products'
}
},
created() {
axios.get('http://jsonplaceholder.typicode.com/posts')
.then(response => {
// JSON responses are automatically parsed.
this.posts = response.data
})
.catch(e => {
this.errors.push(e)
})
//window.addEventListener('load', () => {
setTimeout(() => {
var str = window.location.hash;
var res = str.replace("#bas", "");
var div = document.getElementById(res);
var rect = div.getBoundingClientRect();
$('html, body').animate({
scrollTop: rect.top
}, 500);
}, 100)
//})
}
}
Detail :
Vue Router supports this behavior.
EDIT:
All you need to do is add scrollBehavior to your routerOptions.
export default new Router({
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 };
}
},
routes: []
});

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>