Missing requried props when using beforeEnter() route guard - vue.js

I am trying to fetch data from API using beforeEnter() route guard but I am getting an error:
Missing required prop: "rides"
Here's my code.
router.js
{
path: '/',
name: 'home',
component: () => import('./components/main.vue'),
props: true,
beforeEnter(to, from, next) {
store.dispatch('ride/fetchRides').then(rides => {
to.params.rides = rides
next()
})
}
}
actions.js
fetchRides({ commit, dispatch }) {
return statistcsService.ridesForCurrentWeek()
.then(response => {
commit('SET_RIDES', response.data)
return response.data
})
.catch(error => {
const notification = {
type: 'danger',
message: 'There was a problem fetching your rides'
}
dispatch('notification/add', notification, { root: true })
throw error
})
}
Index.vue
<script>
export default {
props: {
rides: {
type: Array,
required: true
}
}
...
}
</script>
What am I missing? The prop is set in the component so am I not sure why it is crying.
I have verified that in 100% I am getting the data from API response.

You forgot to add rides property in your html code for that component. According to the error message - that's the problem.
Example:
<component :rides="rides"></component>

Related

Nuxt js Router Push Not Working After Delete

I've created a simple CRUD with Nuxt. Data is provided by Lumen. I got a problem with the DELETE, data is deleted but Nuxt does not redirect to the other page.
Here is my script:
<script>
export default {
name: 'EmployeePage',
data() {
return {
fields: ['name','email','image','address'],
emplyees:[],
}
},
mounted() {
this.$axios.get('/employee').then(response => {
this.pegawais = response.data.data
}).catch(error => {
console.log(error.response.data)
})
},
methods: {
async delete(id) {
await this.$axios.delete(`/employee/${id}`).then(response => {
this.$router.push({ name: 'employee' }) <-----this redirect not working
})
}
}
}
</script>
I want Nuxt to redirect to the employee page that display all the data after the deletion.
You should not mix async/await and .then. Use the first approach, that way you will not have the .then callback hell and it will be cleaner overall.
Like this
<script>
export default {
name: 'EmployeePage',
data() {
return {
fields: ['name', 'email', 'image', 'address'],
emplyees: [],
}
},
async mounted() {
try {
const response = await this.$axios.get('/employee')
this.pegawais = response.data.data
} catch (error) {
console.log(error.response.data)
}
},
methods: {
async delete(id) {
await this.$axios.delete(`/employee/${id}`)
await this.$router.push({ name: 'employee' })
},
},
}
</script>
await this.$router.push does not require an await but it's a Promise too, so I'm writing it like that in case you need to call something else afterwards.
this.$axios.$get('/employee') can also be used if you want to remove a .data aka this.pegawais = response.data as shown here.

How get the param from url on Vue.js and use it in router file?

I have router file, which contains all my routes.
Here is an example on one route:
path: '/events/:step',
children: [
{
path: '',
name: 'event.step',
components: {
default: Event,
sidebar: EventSidebar
}
}
],
props: {
default: ({ params, query: { id } }) => ({ id, ...params })
},
components: {
header: () => import('NavBar'),
default: () => import('Event')
},
async beforeEnter(to, from, next) {
if (step === 'login') { // can't find step
// do something
}
next()
}
I need to get the step param from route and if it is login do something in beforeEnter function.
How can I get it?
To get params from route you need to use to.params or from.params, if you want to access path you can get it from to.path or from.path depends what you need.
More info on https://router.vuejs.org/api/#routelocationnormalized
You can register global before guards using router.beforeEach:
router.beforeEach((to, from, next) => {
if (['Login', 'Signup'].includes(to.name) && logged_in)
next({ name: 'Home' })
else next()
})
https://router.vuejs.org/guide/advanced/navigation-guards.html

Vue Router Warning and Router.push is not Functioning

I am trying to route to another page after getting response from adonis project. Calling to post method is working. However router.push('/') is not functioning. Only login page reloaded every time when I submitted the b-from.
async login({ commit, state }) {
console.log("Login")
try {
const response = await HTTP()
.post('/admin/login', {
email: state.loginEmail,
password: state.loginPassword
})
.then(response => {
console.log("Ok")
console.log(response.data)
if (response.data == 'UserNotFoundException') {
alert('User not found')
router.push('/')
}
if (response.data == 'PasswordMisMatchException') {
alert('password not ms')
router.push('/')
}
if (response.data.token) {
console.log(response)
//commit('setToken', response.data.token)
router.push('/')
} else {
router.push('/')
}
})
console.log(response)
//return router.push('/')
} catch (error) {
console.log(error)
}
},
Routes:
routes: [
{
name: "FullLogin",
path: "/login",
component: () => import("#/views/authentication/FullLogin"),
},
{
path: "/",
redirect: "/dashboard/docs-dashboard",
component: () => import("#/layouts/full-layout/FullLayout"),
children: [
{
name: "Dashboard",
path: "/dashboard/docs-dashboard",
component: () => import("#/views/dashboards/docsDashboard"),
},
]
}
]
router.beforeEach((to, from, next) => {
next()
})
I can't figure out why router.push('/') is not routing.
According to docs:
Note: Inside of a Vue instance, you have access to the router instance as $router. You can therefore call this.$router.push.

passing data to route's component beforeEnter

I have the following VueRouter route
{
path: '/playlists/:id',
name: 'videos',
component: Video,
props: {
videos: [],
},
beforeEnter(to, from, next) {
Vue.axios
.get('/playlistitems?playlistId='.concat(to.params.id))
.then((response) => {
to.params.videos = response.data
next((vm) => {
console.log(vm)
vm.videos = response.data
})
})
.catch((err) => console.log('error', err))
},
}
When the route is entered into everything executes as expected but I'm not sure how to pass the response.data to the route's component Videos
Question 1
Can you set the Vue component's props property from the Router?
Question 2
The route is a dynamic route. If the route and component is already loaded and the dynamic parameter changes....does beforeEnter still fire? If not where should I put my data fetching logic? Do I watch for route changes inside the Vue component?
1)
This might not be the most elegant approach, but here's a way to achieve that:
let videos = [];
export default new Router({ ... });
{
path: '/playlists/:id',
name: 'videos',
component: Video,
props: route => ({ videos }),
beforeEnter (to, from, next) {
Vue.axios.get('/playlistitems?playlistId='.concat(to.params.id))
.then((response) => {
// Now the data will be available when the props will be initialized
videos = response.data
next()
})
.catch((err) => console.log('error', err))
}
}
// Videos.vue
props: {
videos: Array
},
2)
IMHO, it would be easier if you could encapsulate the logic in the component.
What do I mean by that is that you could fetch your data in the created hook and set it to a variable that you defined in your data function.
data() {
return {
videos: []
}
},
created () {
Vue.axios.get('/playlistitems?playlistId='.concat(this.$route.params.id))
.then((response) => {
this.videos = response.data;
})
.catch((err) => console.log('error', err))
},
Another approach, which may be suitable or not, depending on what you're working on, would be to have a parent component that fetches multiple playlists which could be stored in vuex.
Thus, you would have another component that handles playlists/:id.
Within this last mentioned component, in your created hook, you would have something like this:
created () {
this.$store.commit('playlist/SET_CURRENT_PLAYLIST_ID', this.$route.params.id);
this.videos = this.$store.getters['playlits/getVideosByPlaylistId'];
},

How can I test data returned from an ajax call in mounted is correctly rendered?

I have a component (simplified)
<template>
<div v-if="account">
<h1 v-text="accountName"></h1>
</div>
</template>
<script>
import repo from '../../repo';
export default {
data() {
return {
account: {}
}
},
mounted() {
return this.load();
},
computed: {
accountName: function () {
return this.account.forename + ' ' + this.account.surname;
}
},
methods: {
load() {
return repo
.get(repo.accounts, {
params: {
id: this.$route.params.id
}
})
.then((response) => {
console.log(response.data);
this.account = response.data;
this.validateObj = this.account;
}, (error) => {
switch (error.response.status) {
case 403:
this.$router.push({name: '403'});
break;
default:
this.$refs['generic_modal'].open(error.message);
}
});
}
}
}
</script>
Which on mount, calls an API, gets the returned data, and renders the forename and surname.
I'm trying to write a mocha test to check that this works. I can do it using a timeout.
it('mounts', done => {
const $route = {
params: {
id: 1
}
};
mock.onGet('/api/accounts/1').reply(200, {
forename: 'Tom',
surname: 'Hart'
});
const wrapper = shallowMount(AccountsEdit, {
i18n,
mocks: {
$route
}
});
setTimeout(a => {
expect(wrapper.html()).toContain('Tom Hart');
done();
}, 1900);
});
But I wondered is there a better way? I was hoping to hook into the axios.get call, and run the check once that's finished, however, I can't seem to figure out how to do it.
EDIT: I tried using $nextTick, however, that didn't work either
wrapper.vm.$nextTick(() => {
expect(wrapper.html()).toContain('Tom Hart');
done();
});
{ Error: expect(received).toContain(expected) // indexOf
Expected substring: "Tom Hart"
Received string: "<div><h1>undefined undefined</h1></div>"
at VueComponent.<anonymous> (/home/tom/Dev/V6/Admin/.tmp/mocha-webpack/1554202885644/tests/Javascript/Components/Pages/account-edit.spec.js:37:1)
at Array.<anonymous> (/home/tom/Dev/V6/Admin/node_modules/vue/dist/vue.runtime.common.dev.js:1976:12)
at flushCallbacks (/home/tom/Dev/V6/Admin/node_modules/vue/dist/vue.runtime.common.dev.js:1902:14)
matcherResult: { message: [Function: message], pass: false } }
{ forename: 'Tom', surname: 'Hart' }
1) mounts
0 passing (2s)
1 failing
1) Accounts Edit Page
mounts:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/tom/Dev/V6/Admin/.tmp/mocha-webpack/1554202885644/bundle.js)
EDIT 2: It seems just as a test, chaining $nextTick eventually works, so I guess something else is causing ticks before my call returns? Is there anyway to tell what caused a tick to happen?
wrapper.vm.$nextTick(() => {
wrapper.vm.$nextTick(() => {
wrapper.vm.$nextTick(() => {
wrapper.vm.$nextTick(() => {
wrapper.vm.$nextTick(() => {
wrapper.vm.$nextTick(() => {
expect(wrapper.find('h1').html()).toContain('Tom Hart');
done();
});
});
});
});
});
});
Hey we had similar problem and found this library:
https://www.npmjs.com/package/flush-promises
Which allow to us wait all promises before continue testing.
Try to do something like this:
const flushPromises = require('flush-promises');
it('mounts', (done) => {
const $route = {
params: {
id: 1
}
};
mock.onGet('/api/accounts/1').reply(200, {
forename: 'Tom',
surname: 'Hart'
});
const wrapper = shallowMount(AccountsEdit, {
i18n,
mocks: {
$route
}
});
flushPromises().then(() => {
expect(wrapper.html()).toContain('Tom Hart');
done();
});
});