I am new to Vue and I'm trying to learn how to apply Vue router. I got normal routing to work no problem. When I try to use dynamic routing everything continued to work fine. When I tried to pass props to dynamic routes however my code breaks.
I'm using these cdn versions of Vue and Vue router which are the versions suggested by the official websites:
- https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js
- https://unpkg.com/vue-router#2.0.0/dist/vue-router.js
The HTML
<div id="app">
<h1>{{ message }}</h1>
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-link to="/user/John">Name</router-link>
</nav>
<!-- route outlet -->
<!-- component matched by route will render here -->
<router-view></router-view>
</div>
The JS
// Route components
const Home = { template: '<div>Home</div>' };
const About = { template: '<div>About</div>' };
const User = { props: ['name'], template: `
<div>
<div>User {{ name }}</div>
<button #click="checkName">Check</button>
</div>`,
methods: {
checkName: function() {
console.log('Params name: ' + this.$route.params.name);
console.log('Props name: ' + this.name);
}
}
};
// Routes for router
const routes = [
{ path: '/', component: Home },
{ path: '/home', redirect: Home },
{ path: '/about', component: About },
{ path: '/user/:name', component: User, props: true }
];
const router = new VueRouter({
routes: routes
});
const app = new Vue({
el: '#app',
data: {
message: 'VueJS Router'
},
router: router
});
When I navigate to the 'Name' page the static text renders fine but the dynamic text fails to load. I added a button that will log the value of name from props and from $route.params to the user. When clicked it turns out that the value of name in props is undefined but the value of name from params is correct. Why is this?
If you're sticking with VueRouter#2.0.0 or lower :
The name that you expect is not passed as a prop but as a route param, cf. Dynamic route matching.
You need to access it from your template as follow : $route.params.name.
You could also use a computed value instead.
If you can update VueRouter
As stated in another answer, and according to the release note of VueRouter#2.2.0, passing down route params as props has only been introduced in v2.2.0, you were using v2.0.0. If you would like to use props you would need to update to (at least) v2.2.0.
CDN link provided on the Vue Router installation page was outdated. Instead of:
https://unpkg.com/vue-router#2.0.0/dist/vue-router.js
use:
https://unpkg.com/vue-router#3.0.1/dist/vue-router.js
Answer provided here:
https://forum.vuejs.org/t/why-is-component-props-undefined-vue-router/34929/5
Related
I am very new to the Vue framework, as well as Javascript, but am currently building a site using Vue and I want to have some links at the top of my site that the user can navigate to. I have tried using the Vue Router (https://router.vuejs.org/guide/#javascript) in order to make these links. At this point, I just want to make a little 'About Us' section that the user can navigate to. But, despite the URL changing accordingly to 'localhost:8080/#/about_us', the Vue component that I have associated with the link will not show up.
I have structured my code in the main.js as such:
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
Vue.config.productionTip = false
export const eventBus = new Vue();
Vue.use(VueRouter);
const AboutUs = {template: '<div>about_us</div>'};
const route = [{path:'/about_us', component: AboutUs}];
const router= new VueRouter({route});
new Vue({
render: h => h(App),
router
}).$mount('#app')
And then I have my app.vue designed as (note: I reduced much of the code to its essentials for brevity):
import AboutUs from './components/AboutUs.vue'
import { eventBus } from './main.js'
export default {
data(){
return {
films: []
}
},
components: {
"about-us": AboutUs
},
mounted(){
fetch('https://ghibliapi.herokuapp.com/films')
.then(res => res.json())
.then(films => this.films = films)
.catch(error=> console.log(error))
}
}
</script>
body {
background-color: deepskyblue;
}
<h1>Ghibli Fandom Extravaganza</h1>
<nav>
<li><router-link to="/about_us">About us </router-link></li>
<router-view></router-view>
</nav>
<p>List of Ghibli Movies: <films-list :films="films"/></p>
<film-detail />
At this point, my AboutUs component is only a very basic Vue that shows some information about the site in some simple HTML tags. But although the link is active and does work, the information from the Vue is not displayed, while the other Vue components continue to show, which indicates that maybe they are not connected? I have tried to follow the tutorial in the Vue Router site, but I don't think that I understand the mechanics of how the code actually works. Can anybody recommend me any corrections?
UPDATE:
Here is the code to my AboutUs.vue
<template>
<div>
<h1>This site is for examining the movies of Studio Ghibli</h1>
</div>
</template>
<script>
export default {
name: 'about-us'
}
</script>
<style scoped>
</style>
I think there is no need to import 'aboutus' component. You can just write like this <router-link to="about_us">About us </router-link>
and in the main.js declare the route like this
const route = [{path:'/about_us',name:'about_us', component: () => import("path to about us file")}];
The code samples you provided are a bit confusing, you should simply pass an imported view straight in to the component property of a router entry.
Where you have done:
const AboutUs = {template: '<div>about_us</div>'};
Replace that line with:
import AboutUs from './components/AboutUs.vue'
I can't figure out from your sample, when and what the relevance of components: {"about-us": AboutUs }, it is not needed.
Here is a sample of my setup:
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'index',
component: () => import('../components/views/welcome')
},
{
path: '/about-us',
name: 'about-us',
component: () => import('../components/views/about-us')
}
]
const router = new VueRouter({
mode: 'history',
routes
})
export default router
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
App.vue
<template>
<v-app v-cloak>
<router-link :to="{ name: 'index' }">Welcome</router-link>
<router-link :to="{ name: 'about-us' }">About Us</router-link>
<router-view></router-view>
</v-app>
</template>
<script>
export default {
name: 'App'
}
</script>
components/views/about-us.vue
<template>
<div>This is the About Us page!</div>
</template>
<script>
export default {
name: 'about-us'
}
</script>
This sample uses History Mode
Other things to note
When routing, mounted is unreliable, instead you should place any fetch logic into it's own method when calling any :
methods: {
fetch () {
// https://github.com/axios/axios
axios.get('https://ghibliapi.herokuapp.com/films').then( ... )
}
}
Call this.fetch method in both beforeRouteUpdate and beforeRouteEnter instead of mounted, you can't even rely on created when it comes to views handled by vue-router.
Axios is suggested instead of native fetch because axios provides more functionality, features and browser compatibility.
In about-us.vue you add these Navigation Guards like so:
<template>
<div>This is the About Us page!</div>
</template>
<script>
export default {
name: 'about-us'
methods: {
fetch () {
axios.get('https://ghibliapi.herokuapp.com/films').then( ... )
}
}
// Will fire if you are already on the view but a parameter changes (dynamic routing)
beforeRouteUpdate(to, from, next) {
this.fetch()
next()
},
// Will fire when you enter the view
beforeRouteEnter(to, from, next) {
this.fetch()
next()
},
}
</script>
Both should be added, understand that they won't fire at the same time, only one of them will execute fetch once when relevant.
This will resolve any issues you would otherwise encounter with Dynamic Routing should you ever use them.
Folder Structure
src/
+ App.vue
+ main.js
+ router.js
+ vue.config.js
+ assets/
+ logo.png
+ components/
+ views/
+ welcome.vue
+ about-us.vue
Hope this clears up the setup requirement for you.
I'm learning VueJS Routing by reading the docs. I opened an example from the page and added my own nested routes as "/user/foo/posts/1, /user/foo/posts/2, /user/foo/posts/3" and expected them to display properly
I've tried reading the starting example carefully, as well as the documentation for routing (the page on Nested Routes). Nothing is cluing me in on whats wrong.
Here's the code:
HTML:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<p>
<router-link to="/user/foo">/user/foo</router-link>
<router-link to="/user/foo/profile">/user/foo/profile</router-link>
<router-link to="/user/foo/posts">/user/foo/posts</router-link>
<!-- stuff I added myself below this line (comment is not present in original code) -->
<router-link to="/user/foo/posts/1">/user/foo/posts/1</router-link>
<router-link to="/user/foo/posts/2">/user/foo/posts/2</router-link>
<router-link to="/user/foo/posts/3">/user/foo/posts/3</router-link>
</p>
<router-view></router-view>
</div>
JS:
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const UserHome = { template: '<div>Home</div>' }
const UserProfile = { template: '<div>Profile</div>' }
const UserPosts = { template: '<div>Posts</div>' }
const PostOne = { template: '<p>Gonna clone Twitter</p>'}
const PostTwo = { template: '<h5>Not gonna clone IG until Im done Twitter</h5'}
const PostThree = { template: '<p>Gonna be a paid web developer</p>'}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
// UserHome will be rendered inside User's <router-view>
// when /user/:id is matched
{ path: '', component: UserHome },
// UserProfile will be rendered inside User's <router-view>
// when /user/:id/profile is matched
{ path: 'profile', component: UserProfile },
// UserPosts will be rendered inside User's <router-view>
// when /user/:id/posts is matched
{ path: 'posts', component: UserPosts,
children: [
// the posts within /posts
{ path: "1", component: PostOne },
{ path: "2", component: PostTwo },
{ path: "3", component: PostThree }
]
}
]
}
]
})
const app = new Vue({ router }).$mount('#app')
Specifically, I'm expecting /1 to say "Gonna clone Twitter", /2 to say "Not gonna clone IG until Im done Twitter" (in h5 tags), and /3 to say "Gonna be a paid web developer".
I expected to see those texts loading under "User foo
Posts" when the links are clicked on, and I didn't.
edit: Note that I've only tried running this inside of a JSFiddle environment. here is the fiddle itself where I want the code to run properly: https://jsfiddle.net/rolandmackintosh/use04pwf/9/
So the problem you are facing is that you are not only nesting routes, you are nesting components. When a route has it's own component plus children routes, there needs to be a <router-view> for the child(ren) to load into. This change to UserPosts will make it work for you:
const UserPosts = { template: '<div>Posts<router-view></router-view></div>' }
I am trying to pass properties to a component through a router-link on Vuejs, but the component is not recieving them properly. I have a main menu and a secondary menu, which is where I want to pass the properties, when a main menu button is clicked:
const Sidebar = {
name:'sidebar',
template:`
<div>
Sidemenu
<router-link v-for="route in routes" v-key="route" :to="route">{{route}}</router-link>
</div>`,
props: {
routes:{
type: Array,
required: true,
default: function(){return []}
}
}
}
const AppMenu = {
name:'app-menu',
template:`
<div>
<router-link :to="{name:'project', params:{routes: projectMenus}}">Project</router-link>
<router-link :to="{name:'assets', params:{routes: assetsMenus}}">Assets</router-link>
</div>`,
data: function(){
return{
projectMenus: ['overview', 'settings'],
assetsMenus: ['landscapes', 'characters']
}
}
}
const App = {
template:`
<div>
<app-menu/>
<router-view name="sideview"/>
<router-view name="sectionview"/>
</div>`,
components:{AppMenu}
};
const Routes= [
{path: '/', name:'home', redirect: 'project'},
{path:'/project', name:'project', components:{sideview:Sidebar}, props:{sideview:true}},
{path:'/assets', name:'assets', components:{sideview:Sidebar}, props:{sideview:true}}
]
The Sidebar component is rendering, because I can see "Sidemenu" on the page, but the console throws [Vue warn]: Missing required prop: "routes" on Sidebar, so my submenu links are not shown. I've searched on the docs, and many similar issues in many forums, but I can't find what I'm doing wrong.
When you make props an object in the vue routes definitions, it will replace the components props. You are setting them as {sideview: true} which overrides all props in your Sidebar component.
At least that is my understanding from https://router.vuejs.org/guide/essentials/passing-props.html#object-mode
My router file
import DomainAction from './components/domainaction/DomainAction.vue'
...
{ path: '/domainaction' , component: DomainAction },
...
router link File
...
<router-link to="/domainaction" tag="li" class="list-group-item "class-active="active" exact > Domain Action</router-link>
...
From another routes going to the domainaction Route like this
...
itemAction(action,data){
if(action=='Suspend'){
this.$router.push('/domainaction')
}
}
...
My Actual DomainAction component
<template>
.......
</template>
<script>
export default {
props:['data'],
data(){
return{
.........
</script>
I want to pass data props from itemAction function .
How do i achieve this ?
I'm new to vue js .Sorry if my question is not very complete.
Well if you just need to pass a property (a value) to the component, you just need to use data passing via the router's props: https://router.vuejs.org/guide/essentials/passing-props.html#function- mode.
And receive using this.$router within the component.
Alternatively you can use the Vuex for data passing.
https://vuex.vuejs.org/
So that you can make a communication between components outside this form that was informed, both components have to be parent and child, ie, 1 calls the other inside it, so yes you can pass the values, otherwise it should be used, or the data path via route, or via vuex which is the vue status manager.
There is also the following possibility, as requested in this question.
Passing props to Vue.js components instantiated by Vue-router
Note that sosmii is right about using a Vuex to share data across components.
But if you're looking into pass soma data as params using routes, you have to change a little bit how you're defining the components routes. Using a routers params you would be able to pass props, then you can push the route and add params. See example below.
And related question:
Passing props with programmatic navigation Vue.js
const DomainActionA = {
props: ['dataProp'],
template: '<div>A data: {{dataProp}}</div>'
}
const DomainActionB = {
props: ['dataProp'],
template: '<div>B data: {{dataProp}}</div>'
}
const routes = [
{
path: '/DomainActionA/:dataProp',
name: 'domainA',
component: DomainActionA,
props: true },
{
path: '/DomainActionB/:dataProp',
name: 'domainB',
component: DomainActionB,
props: true
}
]
const router = new VueRouter({
routes
})
const app = new Vue({
router,
methods: {
goToA() {
this.$router.push({name: 'domainA', params: {dataProp: "blah"}})
},
goToB() {
this.$router.push({name: 'domainB', params: {dataProp: "bleh"}})
}
}
}).$mount('#app')
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello Router!</h1>
<div>
<button #click="goToA()">Go A with params: { data: "blah" }</button>
<button #click="goToB()">Go B with params: { data: "bleh" }</button>
</div>
<div>
<h2>router view</h2>
<router-view></router-view>
</div>
</div>
you should use vuex
props are designed to pass variables from a parent component to child components,
so it is unusual to use it when you want to share data between pages.
an example of right usage of prop:
parent.vue
<child-component :data="data">
<child-component :data="anotherData">
so, if you want to share variables between pages, use vuex (store pattern) instead.
similar question:
https://stackoverflow.com/a/40955110/10440108
I'm trying to wrap my head around Vue - in particular, passing data from the Vue instance to a component. I'm also using Vue-Router and Axios, and it's (meant to be) a pretty simple project, with just one JavaScript file.
The important parts of index.html look like this:
<div id="app">
<!-- Navigation -->
<ul>
<router-link tag="li" to="/" exact><a>Home</a></router-link>
<router-link tag="li" to="/category"><a>Category</a></router-link>
</ul>
<!-- The Content -->
<router-view></router-view>
</div>
Then in main.js, there is the following:
var VideoList = Vue.component('video-list', {
props: ['posts'],
template: '<ul> <li v-for="post in posts"> {{post.id}} </li> </ul>'
});
var router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes: [
{ path: '/', component: VideoList},
{ path: '/category', component: VideoList }
]
});
new Vue({
el: '#app',
data: {
posts: []
},
router: router,
created: function() {
var me = this;
axios.get( '/wp-json/wp/v2/posts' )
.then( function(response){
me.posts = response.data;
})
}
});
The data loads from the AJAX call (I can log it etc), and exists on the Vue instance when I look at it with the Vue devtools add-on. I just can't quite figure out how to pass the data on to the <video-list> component. At the moment I'm getting an error:
The data property "posts" is already declared as a prop. Use prop default value instead.
You would pass props to the router-view, just like you would to any other component.
<router-view :posts="posts" ></router-view>
VideoList should then have access to posts.
Not quite the solution I wanted, but this seems to have done the trick. Putting the data directly onto the component turned out to work in this particular case.
var VideoList = Vue.component('video-list', {
template: '<ul><li v-for="post in posts"><video-post></video-post></li></ul>',
data: function() {
return {
posts: []
}
},
mounted: function() {
var me = this;
axios.get( '/wp-json/wp/v2/posts' )
.then( function(response){
me.posts = response.data;
})
}
})