Nuxt 3 throw createError not reaching the custom error page - javascript

I am settting up an auth middleware which runs just fine, however when I am creating the error I just get to the default error page. I need to reach the custom ~/error.vue to insert a button to click clearError()
Docs says I can just add an error component in the source, which I did.
If ~/error.vue is not reachable from ~/server/middleware then how can I call clearError() in the default error page?
in ~/server/midddleware/auth/auth.ts
export default defineEventHandler(async (event) => {
if(conditionNotMet){
throw createError({
statusCode: 404,
name: 'NotFoundError',
message: 'Todo not found',
statusMessage: "Not Found",
})
}
}
~/error.vue
<template>
<div>{{ props.error }}</div>
</template>
<script setup>
const props = defineProps(['error'])
</script>
This is what I see instead
https://nuxt.com/docs/getting-started/error-handling

Related

Vue Toast not showing?

I am trying to add a toast to my code as per below, however no toast message appears and I dont get any errors in the console. The code works however and the invite gets sent. I have used it like this in other files and the toast message appears so im uncertain as to why it wouldn't appear now.
Main.js file where I am importing toast and toast service:
import Toast from 'primevue/toast';
import ToastService from 'primevue/toastservice';
const app = createApp(App);
app.component('Toast', Toast);
app.use(ToastService);
In my file using the toast once an invite is sent if successful I want to display the success message:
<template>
<div class="main-container">
<Button #click="sendInvites"/>
</div>
</div>
</template>
<script>
export default {
data() {
return {
};
},
methods: {
createToast() {
get('CreateInvites', { invites: this.invites.filter((invite) => invite.email.length > 0) }).then((data) => {
if (data.length > 0) {
this.$toast.add({
severity: 'error',
summary: 'Unable to send some invites',
detail: data
})
.join('\n'),
life: 9000,
});
}
else {
this.$toast.add({
severity: 'success',
summary: 'Success!',
life: 3000,
});
}
});
},
},
The ideal location of a Toast is the main application template so that
it can be used by any component within the application.
So, you need to use the <Toast> component in your main file (App.vue) like this-
<template>
<Toast />
<template>
It means the Toast component is mounted while App is loaded into the DOM and it is ready to be displayed upon any event's trigger as you did-
this.$toast.add({
severity: 'error',
summary: 'Unable to send some invites',
detail: data
.map((detail) => {
return `${detail.email}: ${detail.error}`
})
.join('\n'),
life: 9000,
})
For more information, read here- https://primefaces.org/primevue/toast

How to redirect in Nuxtjs when the return query of Graphql is empty

I'm very new to Nuxtjs and javascript world. The Nuxtjs project I'm working on required fetching data from Backend (Strapi) using Graphql.
The goal is to make a redirect to homepage (index.vue) when Graphql return empty query result.
However, it did not work when I'm using router.push('/') as the code below.
Please note that I'm trying to redirect both on result and mounted block. Both of them did not work at all.
Please help me point out what I'm doing wrong here. Thank you for your kindness in advance
<script>
import getProfiletQuery from '~/apollo/queries/profiles/profile'
export default {
data () {
return {
profiles: {
}
}
},
apollo: {
profiles: {
prefetch: true,
query: getProfiletQuery,
variables () {
return { user: this.$route.params.user }
},
result ({ data }) {
if (data.profiles.length === 0) {
console.log('it enters here!!!')
this.$router.push('/') //this is not working
}
}
}
},
mounted () {
console.log('this.profiles.length: ', this.profiles.length)
if (this.profiles.length === 0) {
this.$router.push('/')
}
}
}
</script>
Old question, but the replies weren't helpful at all and no answers were offered. So in case you never found the answer, here's the best way I know of personally.
The reason your intended method didn't work is because it's unexpected behaviour for the Nuxt router. This is explained in much more detail here:
vue-router — Uncaught (in promise) Error: Redirected from "/login" to "/" via a navigation guard
There are some possible workarounds listed there, but don't use them in this use case.
What you want to do instead, is throw an error, then let Nuxt's built-in error handling take over.
Inside your result(), you need to properly call a Nuxt error like so:
result ({ data }) {
if (data.profiles.length === 0) {
this.$nuxt.error({ statusCode: 404, message: 'Page not found.' })
}
}
Then, you will need to ensure you have an error.vue template file inside your /layouts/ directory. This should contain at a bare minimum:
<template>
<div class="container">
<h1>{{ error.statusCode }}</h1>
<h2>{{ error.message }} </h2>
</div>
</template>
Sorry I didn't see this earlier, and hope you got something working in the meantime! At least now there is an answer there for whoever comes looking next!

Unhandled error during execution of scheduler flush. This is likely a Vue internals bug

I am getting the following error while creating slides in Vue.js:
[Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next
at <Anonymous key=1 >
at <Anonymous pager="true" options= {initialSlide: 1, speed: 400} >
at <Anonymous fullscreen=true >
at <IonPage isInOutlet=true registerIonPage=fn<registerIonPage> >
at <Product Details ref=Ref< Proxy {…} > key="/products/1" isInOutlet=true ... >
at <IonRouterOutlet>
at <IonApp>
at <App>
Uncaught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
Uncaught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
at insert (webpack-internal:///./node_modules/#vue/runtime-dom/dist/runtime-dom.esm-bundler.js:222:16)
at mountElement (webpack-internal:///./node_modules/#vue/runtime-core/dist/runtime-core.esm-bundler.js:3958:9)
at processElement (webpack-internal:///./node_modules/#vue/runtime-core/dist/runtime-core.esm-bundler.js:3899:13)
at patch (webpack-internal:///./node_modules/#vue/runtime-core/dist/runtime-core.esm-bundler.js:3819:21)
at componentEffect (webpack-internal:///./node_modules/#vue/runtime-core/dist/runtime-core.esm-bundler.js:4312:21)
at reactiveEffect (webpack-internal:///./node_modules/#vue/reactivity/dist/reactivity.esm-bundler.js:71:24)
at effect (webpack-internal:///./node_modules/#vue/reactivity/dist/reactivity.esm-bundler.js:46:9)
at setupRenderEffect (webpack-internal:///./node_modules/#vue/runtime-core/dist/runtime-core.esm-bundler.js:4277:89)
at mountComponent (webpack-internal:///./node_modules/#vue/runtime-core/dist/runtime-core.esm-bundler.js:4235:9)
at processComponent (webpack-internal:///./node_modules/#vue/runtime-core/dist/runtime-core.esm-bundler.js:4195:17)
If I add the slides hard coded it does not show any errors. But if I add the slides dynamically using a v-for loop then it shows the above errors.
I have added the slides in following way:
This is the template:
<ion-slides pager="true" :options="slideOpts">
<ion-slide v-for="image in product.product_images" v-bind:key="image.id">
<h1>Slide 1</h1>
</ion-slide>
</ion-slides>
This is the script:
export default {
name: "Product Details",
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonSlides,
IonSlide,
},
data() {
return {
product: {},
};
},
setup() {
// Optional parameters to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options.
const slideOpts = {
initialSlide: 1,
speed: 400,
};
return { slideOpts };
},
mounted: function () {
fetch("http://localhost:4000/api/products/" + this.$route.params.id, {
method: "get",
})
.then((response) => {
return response.json();
})
.then((jsonData) => {
this.product = jsonData;
// console.log(jsonData.product_images);
});
},
};
What am I doing wrong in the code?
Arguably, the error message could be improved on this one.
The error was caused by trying to iterate through a non-iterable (in your case undefined), using v-for. Specifically, before the call made in mount() returns, product.product_images is undefined, because you initiate product as empty object.
Vue 2 style solutions
instantiate product.product_image as iterable:
//...
data: () => ({
product: { product_images: [] }
})
or provide an empty array as fallback in template:
<ion-slide v-for="image in product.product_images || []" v-bind:key="image.id">
<h1>Slide 1</h1>
</ion-slide>
or place a v-if on the parent of the v-for:
<ion-slides pager="true" :options="slideOpts" v-if="product.product_images">
...
</ion-slides>
Vue 3 style solution
Make the entire ProductDetails component suspensibe, by giving it an async setup function. In the setup function, make the call to get the product.
Proof of concept:
//...
async setup() {
const product = await fetch("http://localhost:4000/api/products/" +
this.$route.params.id, {
method: "get",
}).then(r => r.json());
return { product }
}
Now place <product-details> into a <Suspense>'s <template #default>, providing a fallback template (which will be rendered while <Suspense> resolves all the async components found in its default template):
<Suspense>
<template #default>
<product-details></product-details>
</template>
<template #fallback>
Product is loading...
</template>
</Suspense>
The beauty (and elegance) of using <Suspense> is that the parent doesn't need to know the actual condition(s) for which the markup is not yet to be rendered. It simply waits for all suspensible components to resolve.
In short, using <Suspense> you no longer need to hardcode the rendering logic into the template using v-ifs and specifying the condition in clear, on the wrapper. Each async child component contains its own condition and all it announces to the parent is: i'm done. When all are done, they're rendered.
This fixed this error for me.
With error :
describe('App', () => {
it('test 1', () => {
const wrapper = shallowMount(Component)
expect(wrapper.find('h2').text()).toEqual("my text")
})
it('test 2', () => {
const wrapper = shallowMount(Component)
expect(wrapper.find('h3').text()).toEqual("my othertext")
})
})
Without :
describe('App', () => {
const wrapper = shallowMount(Component)
it('test 1', () => {
expect(wrapper.find('h2').text()).toEqual("my text")
})
it('test 2', () => {
expect(wrapper.find('h3').text()).toEqual("my othertext")
})
})
Can you change the component name ?
export default
name: "Product Details" ... <= here
Also ran into this error when having malformed hooks (created, mounted, etc...). In my case, created was an object instead of a function.
BAD:
created: { /* some code */ }
GOOD:
created() { /* some code */ }
// or...
created: function() { /* some code */ }

Uncaught (in promise) in vue-router when multiple clicks on a button

I am working with vuejs and vue-router in Rails app. I have a button which calls a nivagate method. When user clicks on this navigate method, it hits an API enpoint via axios module and move to next component using this.$router.push({name: "RouteName"}). Problem is, when user clicks multiple on this button it gives Uncaught (in promise) undefined error in console. My guess is this error occurs because of vue-router.
My question is, how can I catch or handle this error. I am using multiple such buttons in my app so I need a generic solution.
Home.vue - component
<template>
<div>
<button
id="select_button"
#click="onSelectClick"
>
Select
</button>
</div>
</template>
<script>
export default {
onSelectClick() {
this.$`enter code here`axios.get('some-url').then(res => {
//print res on console
}).catch(err => {
// print err on console
})
this.$router.push({ name: 'route-name' }); //route-name != home
}
}
</script>
<style>
</style>
Error I get when I double(or more)-click on button
Uncaught (in promise) undefined
Error Cause: vue-router causes this error when we call this.$router.push({name: 'route-name'}) back to back multiple times without completing the previous call.
Solution: I can handle this error by adding a catch guard on router.push.
this.$router.push({name: 'router-name'}).catch(error => { //handle error here})
I made it generic by adding a method in vue mixin.
import router from '../router'
Vue.mixin({
methods: {
navigate(route) {
router.push(route).catch(err => { //handle error here });
}
}
});
Now I can use this navigate method in my components like this.
<template>
</template>
<script>
export default {
mounted() {
this.navigate({name: 'Home'})
}
}
</script>
<style>
</style>
Hope it helps others.
next({name: 'Login'})
causes Uncaught (in promise)...
I replaced it with
router.push({name: 'Login'})
return
and no error!

Vue-Snotify notification is not showing up

I must implement the library Vue-Snotify to show notifications in a Vue.js project.
Notice: I have no pre-experience with Vue.js! I just got this task and it needs to be done.
I have played around with the project and tried different approaches, which got me to understand Vue.js a bit better but after trying so much, I'm left with no idea how to fix this :(
home.js
import Snotify from 'vue-snotify'
Vue.use(Snotify)
var vmHome = new Vue({
...
components: {
myFancyComponent,
...
}
})
myFancyComponent.vue
<template>
<div>
<button type="button" #click="showNotif">Show</button>
<vue-snotify></vue-snotify>
</div>
</template>
<script>
module.exports = {
name: "my-fancy-component",
methods: {
showNotif: function() {
console.log(this.$snotify.success('Example body content'))
}
},
...
}
</script>
I can trigger the notification method and it even returns me a valid object - no errors! But no trace of a notification.
SnotifyToast {id: 338737384058, title: null, body: "Example body content", config: {…}, eventEmitter: Vue$3, …}
Though I noticed an error in the console on the page's first load:
[Vue warn]: Error in created hook: "TypeError: Cannot read property 'emitter' of undefined"
found in
---> <VueSnotify>
<MyFancyComponent>
<Root>
TypeError: Cannot read property 'emitter' of undefined
at VueComponent.created
I wonder why the VueSnotify tag wraps the MyFancyComponent and not the other way around?
It seems that you got the methods data type wrong, it needs to be an object and not array.
module.exports = {
name: "my-fancy-component",
//---------v--- This part
methods: {
showNotif: function() {
console.log(this.$snotify.success('Example body content'))
}
},
...
}

Categories