Vue js props value comes undefined - javascript

I may be wrong in my understanding of props but I can't seem to be able to pass a prop to a component and retrieve its value since it always comes as undefined.
Route:
{
path: '/account/:username',
name: 'acccount',
component: Account
},
Router-link to the component:
<router-link class="btn btn-warning margin-bottom-20" :to="{ name: 'acccount', params: {username: user.username}}">Edit</router-link>
Component:
export default {
name: 'account',
props: ['username'],
computed: {
user() {
return this.$store.getters.getUser(this.username);
}
}
}
My /accounts view succefully changes to /account/{username} but once on the component the value this.username returns nothing but undefined.

Update your route:
{
path: '/account/:username',
name: 'acccount',
component: Account
}
To:
{
path: '/account/:username',
name: 'acccount',
component: Account,
props: true
}

Related

Vue Router : Cant define children for default OR main components

First of all I'm learning Vue JS 2 since about a week and I'm stuck on a problem with Vue Router. I'm pretty sure the solution is right in front of my eyes but I wasn't able to find it yet.
So : On my root route I have 2 views in components Home and NotAuth.
My aim is to display the Login component and others routes related to it when the user isn't logged.
And when the user logs in the Home component is displayed on '/' and all login related routes are not accessible by him.
It works when I'm logged if I try to access '/register' Im instantly redirected to 'Home'.
But when I'm not logged if I try to access say '/add_device' a blank page is displayed.
What I'd like to do is define other routes as Home's children but I cant find anything in the doc explaining how to do so.
I've tried several things and none worked and I'm kind of lost on how to proceed.
If anyone has a clue on how to proceed that would be awesome.
Thanks !!
index.js
const routes = [
{
path: '/',
name: '',
components: {
login: NotAuth,
default: Home
},
children: [
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/register',
name: 'Register',
component: Register
},
{
path: '/reset',
name: 'ResetPassword',
component: ResetPassword
},
{
path: '/reset-sent',
name: 'ResetSent',
component: ResetSent
},
{
path: '/change-password/:token/:mail',
name: 'ChangePassword',
component: ChangePassword
},
{
path: '/confirmation',
name: 'Confirmation',
component: Confirmation
}
]
},
{
path: '/add_device',
name: 'device',
component: Device
},
{
path: '/add_scene',
name: 'scene',
component: Scene
},
{
path: '/add_room',
name: 'room',
component: Room
},
]
App.vue
<div id="app">
<router-view v-if='userAuthenticated' :username='this.user.first_name' :user='this.user'></router-view>
<router-view name="login" v-else #isAuth='handle'></router-view>
</div>

How can I access props passed in Vue router, inside a Vue component?

I have a route defined below:
{
path: 'fit-details',
name: 'fit-details',
component: Fitment,
props: true
},
I'm passing props through my route with data from the state:
this.$router.push({ path: 'fit-details', query: {
year: this.query.year,
make: this.query.make,
model: this.query.model
}
})
I see the route getting properly defined when passed: www.placeholder.com?year=2008&make=Acura&model=MDX
And finally inside my receiving component I have the props instantiate:
export default {
props: ['year', 'make', 'model'],
},
However I can't access this data in the component for some reason. Any help would be much appreciated, Im honestly not the best when it comes to Vue router stuff
No need to define that props inside the visited component, you could use this.$route.query.year and this.$route.query.make or you could do something like :
const {'year', 'make', 'model'}=this.$route.query
or :
{
path: 'fit-details',
name: 'fit-details',
component: Fitment,
props:(route) => ({ year: route.query.year,make:route.query.make,model:route.query.model })
},
in Fitment component :
export default {
props: ['year', 'make', 'model'],
},

VueJS push a route with params data

I am very new to VueJS. How can I get the deviceId in Device component in vuejs. The deviceId in h1 tag was not printed out in the Device component page.
goForward() {
console.log("go forward");
this.$router.push({ name: "Device", params: { deviceId: "Air-conditioning" } });
},
<template>
<div class="about">
<h1>This is the device page {{ deviceId }}</h1>
</div>
</template>
<script>
export default {
name: "Device",
props: ["deviceId"],
data() {
return {};
},
};
</script>
const routes = [
{
path: '/device',
name: 'Device',
component: Device,
},
]
In order to receive your params as props you need to add the props: true option in the route object.
const routes = [
{
path: "/device",
name: "Device",
component: 'Device',
props: true
}
];
https://router.vuejs.org/guide/essentials/passing-props.html#boolean-mode
It's also worth noting that you can improve the URL scheme a bit by adding a route parameter like so:
{
path: "/device/:deviceId",
...
}
Thus, the URL in the address bar will look cleaner:
https://www.example.com/device/Air-conditioning

Props not updated when set from router

When I set a component props in the route definition the watcher doesn't work and the view is never updated.
In this example the update works correctly :
const Home = { template: '<div>Home</div>' }
const Test = {
template: '<div>Test : {{ nb }}<br><button #click="addOne" type="button">+1</button></div>',
props: {nb: Number},
methods: {
addOne() {
this.nb++
}
}
}
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ name: 'test', path: '/test/:nb', component: Test, props: true }
]
})
new Vue({
router,
el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link :to="{ name: 'test', params: { nb: 42 }}">/test</router-link>
<router-view></router-view>
</div>
Note : This example show a "mutating a prop directly" warning, but it's just for the simplicity it's not related to the issue since I don't do that in my code.
In this example the update doesn't work :
const Home = { template: '<div>Home</div>' }
const Test = {
template: '<div>Test : {{ counter.value }}<br><button #click="addOne" type="button">+1</button></div>',
props: {counter: Object},
methods: {
addOne() {
this.counter.value++
console.log('new value : ' + this.counter.value)
}
}
}
class Counter {
constructor(value) {
this.value = value
}
}
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{
name: 'test',
path: '/test/:nb',
props: (route) => ({counter: new Counter(route.params.nb)}),
component: Test
}
]
})
new Vue({
router,
el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link :to="{ name: 'test', params: { nb: 42 }}">/test</router-link>
<router-view></router-view>
</div>
The counter object won't be reactive, so property changes won't be detected. Passing an object as a prop does not add reactivity.
I've just changed one line in your example:
props: (route) => ({counter: Vue.observable(new Counter(route.params.nb))}),
Passing the object to Vue.observable will apply the reactivity.
Note that 'applying reactivity' means rewriting all of the properties to use getters and setters. In the simple example presented here that just means the value property, which should be fine. Vue can only rewrite properties it can see, so any data hidden within closures would not be accessible.
const Home = { template: '<div>Home</div>' }
const Test = {
template: '<div>Test : {{ counter.value }}<br><button #click="addOne" type="button">+1</button></div>',
props: {counter: Object},
methods: {
addOne() {
this.counter.value++
console.log('new value : ' + this.counter.value)
}
}
}
class Counter {
constructor(value) {
this.value = value
}
}
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{
name: 'test',
path: '/test/:nb',
props: (route) => ({counter: Vue.observable(new Counter(route.params.nb))}),
component: Test
}
]
})
new Vue({
router,
el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link :to="{ name: 'test', params: { nb: 42 }}">/test</router-link>
<router-view></router-view>
</div>
Part of the reason why Vue does not automatically apply reactivity to objects passed as props is that the receiving component shouldn't be modifying the object anyway. It's a violation of one-way data flow. In this example you should probably be updating the route directly rather than modifying the counter object.

Change route name and component if user is logged in

I have a functioning login component in my nativescript-vue that utilizes the RadSideDrawer.
All I want to do is change the Login route to Logout and I can't seem to figure out how to achieve that. Ternary operators wouldn't fully work.
I also tried declaring a new empty array and put my menu array in it and manipulate that instead of the original data. This did not work either.
I would need to close the app and open it again for the menu item to change, it wasn't reactive.
First of all, here my app.js
Vue.prototype.$routes = routes
new Vue({
store,
render (h) {
return h(
App,
[
h(DrawerContent, { slot: 'drawerContent' }),
//h(store.getters.loggedIn ? routes.Home : routes.Login, { slot: 'mainContent' })
h(routes.Home, { slot: 'mainContent' })
]
)
}
}).$start()
And here's my router in router/index.js where I'm declaring the routers.
import Home from "../components/Home";
import Browse from "../components/Browse";
import Featured from "../components/Featured";
import Search from "../components/Search";
import Settings from "../components/Settings";
import Tasks from "../components/Tasks";
import Login from "../components/Login";
import Logout from "../components/Logout";
const routes = {
Home,
Browse,
Featured,
Search,
Settings,
Tasks,
Login,
Logout
}
export default routes
I am also using a $store with a bunch of getters, setters, mutations and actions and one of those getters retrieves if the user is logged in
export const store = new Vuex.Store({
state: {
token: LS.getItem('access_token') || null,
filter: 'all',
todos: [],
msg: ''
},
getters: {
loggedIn(state) {
return state.token !== null
},
}
}
And since I'm using the Drawer navigation, here's DrawerContent.vue
<template lang="html">
<GridLayout rows="auto, *" class="sidedrawer sidedrawer-left">
<StackLayout row="0" class="sidedrawer-header">
<Label class="sidedrawer-header-image fa" text.decode=""></Label>
<Label class="sidedrawer-header-brand" text="User Name"></Label>
<Label class="footnote" text="username#mail.com"></Label>
</StackLayout>
<ScrollView row="1" class="sidedrawer-content">
<StackLayout>
<GridLayout
columns="auto, *"
:class="'sidedrawer-list-item' + (selectedPage === page.name ? ' selected': '')"
v-for="(page, i) in pages"
:key="i"
#tap="goToPage(page.component)">
<Label col="0" :text="page.icon" class="fa"></Label>
<Label col="1" :text="page.name" class="p-r-10"></Label>
</GridLayout>
</StackLayout>
</ScrollView>
</GridLayout>
</template>
<script>
import * as utils from "~/shared/utils";
import SelectedPageService from "~/shared/selected-page-service";
import Login from "./Login";
import Featured from "./Featured";
export default {
data () {
return {
selectedPage: "",
pages: [
{
path: '/',
name: 'Home',
icon: "\uf015",
component: this.$routes.Home
},
{
path: '/browse',
name: 'Browse',
icon: '\uf25a',
component: this.$routes.Browse,
meta : {
requiresAuth: true
}
},
{
path: '/featured',
name: 'Featured',
icon: '\uf005',
component: this.$routes.Featured
},
{
path: '/search',
name: 'Search',
icon: '\uf002',
component: this.$routes.Search
},
{
path: '/settings',
name: 'Settings',
icon: '\uf013',
component: this.$routes.Settings
},
{
path: '/tasks',
name: 'Tasks',
icon: '\uf0ae',
component: this.$routes.Tasks
},
{
path: '/login',
name: 'Login',
icon: '\uf007',
component: this.$routes.Login
}
]
};
},
mounted() {
SelectedPageService.getInstance().selectedPage$
.subscribe((selectedPage) => this.selectedPage = selectedPage);
},
methods: {
goToPage (pageComponent) {
this.$navigateTo(pageComponent, {
clearHistory: true
});
utils.closeDrawer();
},
},
computed: {
loggedIn() {
return this.$store.getters.loggedIn
},
}
};
</script>
As you can see, here is where the routes are actually defined in a pages array and then looped over in the GridLayout above.
The problem with this is that it leaves little room for wiggle as I have to deal with data coming from the loop itself if(page.name === 'Login') { page.name = 'Logout' }
This was my initial attempt, using the v-if directive. But I quickly abandoned it as the statement would be too long and would be kinda of messy.
I also tried to remove the loop and just hard-code the entire menu, but I was unable to access this.$routes.Home in the markup.
At this point I'm really lost. I'm open to any suggestion you might have.
Make the pages property a getter so you can push into the array easily:
computed: {
pages: function() {
const pages = [{
path: '/',
name: 'Home',
icon: "\uf015",
component: this.$routes.Home
},
{
path: '/browse',
name: 'Browse',
icon: '\uf25a',
component: this.$routes.Browse,
meta: {
requiresAuth: true
}
},
{
path: '/featured',
name: 'Featured',
icon: '\uf005',
component: this.$routes.Featured
},
{
path: '/search',
name: 'Search',
icon: '\uf002',
component: this.$routes.Search
},
{
path: '/settings',
name: 'Settings',
icon: '\uf013',
component: this.$routes.Settings
},
{
path: '/tasks',
name: 'Tasks',
icon: '\uf0ae',
component: this.$routes.Tasks
},
]
if (!!this.$store.getters['loggedIn']) {
pages.push({
path: '/logout',
name: 'Logout',
icon: '....',
component: this.$routes.Logout
})
} else {
pages.push({
path: '/login',
name: 'Login',
icon: '\uf007',
component: this.$routes.Login
})
}
return pages
}
}

Categories