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.
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
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-if
s 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.