Reload vue-owl-carousel after dynamic data update - javascript

Initialized a vue-owl-carousel using dynamic data
<script>
import carousel from 'vue-owl-carousel'
export default {
components: { carousel },
}
<carousel :items="1">
<img v-for="(img, index) in images" :src="img" :key="index">
</carousel>
data: function() {
return {
images: [
"https://placeimg.com/200/200/any?1",
"https://placeimg.com/200/200/any?2",
"https://placeimg.com/200/200/any?3",
"https://placeimg.com/200/200/any?4"
]
};
}
Removed one of images from the images array
How to update the owl carousel with new images array?

This should work with normal reactivity so im guessing the way how the carousel library is built is just sloppy. My advice is to just build your own carousel. It is really not that and can be a good learning curve.
Nevertheless if you really want to use this library, you could wrap the carousel in a template with a v-if condition. Because when deleting a entry from the images array the carousel needs to be rerendered to show its changes. You can force this by deleting and adding the component to the DOM by toggling the v-if of the template tag. A hacky solution to a problem that shouldnt exist in the first place. But something like this would work:
<template>
<div id="app">
<template v-if="toggle">
<carousel :items="1">
<template v-for="(img, index) in images">
<img :src="img" :key="index" />
</template>
</carousel>
</template>
<button #click="deleteImage">DELETE A IMAGE</button>
</div>
</template>
<script>
import Carousel from "vue-owl-carousel";
export default {
name: "App",
components: { Carousel },
data() {
return {
images: [
"https://placeimg.com/200/200/any?1",
"https://placeimg.com/200/200/any?2",
"https://placeimg.com/200/200/any?3",
"https://placeimg.com/200/200/any?4",
],
toggle: true,
};
},
methods: {
deleteImage() {
this.toggle = false;
this.images.pop();
this.$nextTick(() => {
this.toggle = true;
});
},
},
};
</script>
Ive tested it and it works with the vue-owl-carousel library see sandbox

Related

How to correctly use $vuetify.goTo with v-lazy components?

I'm trying to lazy load few components into my website using Vue2 and Vuetify. Each component takes about one screen size on my desktop browser and vuetify.goTo works perfectly fine there.
Problem occurs when I try to test it on my mobile. On a first click I'm taken to previous element. Second click (when elements are loaded) leads to right place. I expected that my function will load all previous components and on desktop it seems to work fine. Could you please help me find solution to this issue?
On a mobile second element gets bigger about 150-200vh - maybe it causes troubles? - it's a gallery similar to this from docs), but I have tried to modify min-height of v-lazy with no luck.
Here is my code:
<template>
<v-app>
<v-main>
<v-btn v-for="item in items" :key="item.component" #click="goToItem(item.component)">
{{ item.component }}
</v-btn>
<v-lazy v-for="item in items" :key="item.component" :id="item.component" v-model="item.isActive"
:options="{ threshold: .9 }" :min-height="item.component === 'contact' ? '300' : '50vh'">
<v-componenet :is="item.component" />
</v-lazy>
</v-main>
</v-app>
</template>
<script>
import Intro from '#/components/Intro';
import Contact from '#/components/Contact';
import Projects from '#/components/Projects';
import Trainings from '#/components/Trainings';
export default {
name: 'App',
components: {
Contact,
Intro,
Projects,
Trainings,
},
data: () => ({
items: [{component: 'intro',isActive: false,},{component: 'projects',isActive: false,},{component: 'trainings',isActive: false,},{component: 'contact',isActive: false,},],
}),
methods: {
goToItem(destination) {
for (const item of this.items) {
item.isActive = true
if (item.component === destination) {
this.$nextTick(() => {
this.$vuetify.goTo('#' + item.component)
})
return
}
}
},
},
};
</script>
https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VLazy/VLazy.ts
The height is definetly an issue. If you look at the source-code of v-lazy, the active is just enabling the IntersectionObserver. The isIntersecting is false though, because the component is not in the view. So isActive is also false. Maybe have a look at the devtools an the isActive state of the component.
A small reproduction would be ns here.

Why does boostrap vue toaster dissapears immediately?

I am using Laravel + Vue.js to create a SPA. In the SPA, I am creating a form where user can write markdown content in it and click button to submit it. I want to show an error toaster at the bottom right corner of the screen if the user didn't input any content when they clicked the send button.
I am using boostrap vue toast to implement the error toaster.
However, when I clicked the send button, the error toaster will just blink for 1 second and dissapear immediately. Also, the error toaster blinks at top left corner which is different from what I wrote in the code below.
The mixin that contains the method to invoke the toast:
ErrorMessage.vue
# ErrorMessage.vue
<script>
export default {
methods: {
showErrorMessage (msg) {
this.$bvToast.toast(msg, {
title: ["Error!"],
variant: "danger",
toaster: "b-toaster-bottom-right"
});
}
}
};
</script>
I imported the above mixin in this vue component ArticleForm.vue.
ArticleForm.vue
<template>
<form #submit.prevent="passArticleData">
<div id="editor-markdown-editor">
<div id="editor-markdown">
<div
id="editor-markdown-tag-input"
#click="toggleTagModal"
>
<ul v-if="insertedTags.length">
<li v-for="tag in insertedTags"
:key="tag.id"
class="tag"
>
{{ tag.name }}
</li>
</ul>
<span v-else>タグを入力してください</span>
</div>
<div id="editor-markdown-textarea">
<textarea :value="input" #input="update" ref="textarea"></textarea>
<div id="drop-here"></div>
</div>
</div>
<div id="editor-preview">
<article v-html="compiledMarkdown(input)" class="markdown-render" ref="articleHtml"></article>
</div>
</div>
</form>
</template>
<script>
import _ from "lodash";
import ErrorMessage from "./mixins/ErrorMessage";
import { markdownTable } from "markdown-table";
export default {
props: {
article: Object,
tags: Array
},
mixins: [ErrorMessage],
data () {
return {
articleToPass: {
title: "",
body: "",
isDraft: true
},
input: "",
tagsToPass: [],
insertedTags: [],
tagModalOpen: false
};
},
methods: {
update: _.debounce(function (e) {
this.input = e.target.value;
}, 300),
passArticleData (e) {
// Get title from input
try {
this.articleToPass.title = this.$refs.articleHtml.getElementsByTagName("h1").item(0).innerHTML;
} catch (e) {
this.showErrorMessage(["Body md can't be blank"]);
}
// Remove first line(title) from users' input
this.articleToPass.body = this.input.substring(this.input.indexOf("\n") + 1);
// tag id of written article
const tagIds = this.insertedTags.map(obj => obj.id);
this.tagsToPass = tagIds;
this.$emit("handle-new-data", this.articleToPass, this.tagsToPass);
}
}
Parent component of the above vue component:
ArticleCreate.vue
<template>
<div id="content-area">
<header-component></header-component>
<main id="editor-area">
<article-form :article="article" :tags="tags" #handle-new-data="postArticle"></article-form>
</main>
</div>
</template>
<script>
import HeaderComponent from "./Header.vue";
import ArticleForm from "./ArticleForm.vue";
export default {
data () {
return {
article: {
title: "",
body: "",
is_draft: true
},
tags: []
};
},
components: {
HeaderComponent,
ArticleForm
},
methods: {
postArticle (articleObj, tagsData) {
const data = { article: articleObj, tags: tagsData };
axios.post("/api/articles", data)
.then((res) => {
this.$router.push({ name: "article.show", params: { article: res.data } });
});
}
}
};
</script>
I tried:
changed this.$bvToast to this.$root.$bvToast (based on this issue)
downgrade my bootstrap version from 4.6.0 to 4.5.3 (based on this question)
I have spent trying to solve this but failed. Does anyone know why is this happening and how can I make it work? Please let me know if you need any extra information. Thank you in advanced.
As stated in this comment on stackoverflow that is usually a sign of a bootstrap version mismatch.
I was actually able to reproduce that issue and also fix it with rolling back to bootstrap v4
Broken with bootstrap 5
https://codesandbox.io/s/bootstrap-vue-toasting-broken-with-bootstrap-5-bqe2c
Working with bootstrap 4
https://codesandbox.io/s/bootstrap-vue-toasting-working-with-bootstrap-4-jk2jl
I have figured out the problem.
The problem is that bootstrap-vue css is not loaded properly in my project.
Just add
import "bootstrap-vue/dist/bootstrap-vue.css";
to app.js and it works perfectly fine now.
Thank you

How to stop rendering a vue carousel component after all it's data is shown

As title says, I want to stop rendering a vue carousel component after it's data is shown or after x seconds. I haven't found any related information about this. I know it's quite... weird to do this, but still I want to know how to, for some projects. Here's the code:
<div class="col-md-12 col-sm-12 col latest-news max-width">
<carousel
:per-page="1"
:mouse-drag="false"
:autoplay="true"
:paginationEnabled="false"
:loop="true"
:speed="1500"
:autoplayTimeout="7000"
>
<slide v-for="post in posts_1" :post="post" :key="post.id">
<NewsTitle class="most-important" :post="post" :key="post.id" />
</slide>
</carousel>
</div>
Is this what do you need?
<template>
<carousel
v-if="showCarousel"
...
>
...
</carousel>
</template>
<script>
export default {
data: function(){
return {
showCarousel: true,
}
},
methods: {
hideCarousel: function(){
setTimeout(() => {
this.showCarousel = false;
}, 5000);
}
},
created: function(){
this.hideCarousel();
}
}
</script>
If you want to hide your carousel you should be dependent on showCarousel variable. Also you need to add condition v-if="showCarousel" to your carousel tag. My code should hide carousel after 5000 milliseconds or 5 seconds.

Vue JS - Problem with computed property not updating

I am quite new with VueJS and I have been having trouble lately with some computed properties which do not update as I would like. I've done quite some research on Stack Overflow, Vue documentation and other ressources but i haven't found any solution yet.
The "app" is basic. I've got a parent component (Laundry) which has 3 child components (LaundryMachine). The idea is to have for each machine a button which displays its availability and updates the latter when clicked on.
In order to store the availability of all machines, I have a data in the parent component (availabilities) which is an array of booleans. Each element corresponds to a machine's availability.
When I click on the button, I know the array availibities updates correctly thanks to the console.log. However, for each machine, the computed property "available" does not update is I would want it to and I have no clue why.
Here is the code
Parent component:
<div id="machines">
<laundry-machine
name="AA"
v-bind:machineNum="0"
v-bind:availableArray="this.availabilities"
v-on:change-avlb="editAvailabilities"
></laundry-machine>
<laundry-machine
name="BB"
v-bind:machineNum="1"
v-bind:availableArray="this.availabilities"
v-on:change-avlb="editAvailabilities"
></laundry-machine>
<laundry-machine
name="CC"
v-bind:machineNum="2"
v-bind:availableArray="this.availabilities"
v-on:change-avlb="editAvailabilities"
></laundry-machine>
</div>
</div>
</template>
<script>
import LaundryMachine from './LaundryMachine.vue';
export default {
name: 'Laundry',
components: {
'laundry-machine': LaundryMachine
},
data: function() {
return {
availabilities: [true, true, true]
};
},
methods: {
editAvailabilities(index) {
this.availabilities[index] = !this.availabilities[index];
console.log(this.availabilities);
}
}
};
</script>
Child component:
<template>
<div class="about">
<h2>{{ name }}</h2>
<img src="../assets/washing_machine.png" /><br />
<v-btn color="primary" v-on:click="changeAvailability">
{{ this.availability }}</v-btn>
</div>
</template>
<script>
export default {
name: 'LaundryMachine',
props: {
name: String,
machineNum: Number,
availableArray: Array
},
methods: {
changeAvailability: function(event) {
this.$emit('change-avlb', this.machineNum);
console.log(this.availableArray);
console.log('available' + this.available);
}
},
computed: {
available: function() {
return this.availableArray[this.machineNum];
},
availability: function() {
if (this.available) {
return 'disponible';
} else {
return 'indisponible';
}
}
}
};
</script>
Anyway, thanks in advance !
Your problem comes not from the computed properties in the children, rather from the editAvailabilities method in the parent.
The problem is this line in particular:
this.availabilities[index] = !this.availabilities[index];
As you can read here, Vue has problems tracking changes when you modify an array by index.
Instead, you should do:
this.$set(this.availabilities, index, !this.availabilities[index]);
To switch the value at that index and let Vue track that change.

VueJS Routing the same component not triggering OWL Carousel

Hello
I'm facing this problem with re-evaluating the images rendered by VueJS into a Carousel plugin (OwlCarousel) while loading the same component with different variables
the problem is when loading the page with images everything works well and the carousel can show the images, but when clicking on a link to go to the same component with other images, the carousel shows only an empty box.
Here is what I have so far:
<template>
<div>
<div id="owl-work">
<div class="item" v-for="image in project.images" :key="image.id">
<figure><img :src="'uploads/'+image.url" alt=""></figure>
</div>
</div>
<div class="similars" v-for="similar in similars">
<router-link :key="$route.fullPath" :to="{ name: 'project', params: { id: similar.id, project:similar }}" replace>
<h4>{{similar.title}}</h4>
</router-link>
</div>
</div>
</template>
<script>
export default {
props: ["id"],
computed: {
...mapGetters(['getProject', 'getSimilars']),
project() {
return this.getProject(this.id)
},
similars() {
return this.getSimilars(this.id)
}
}
}
$("#owl-work").owlCarousel();
</script>
and my routes look like:
[
{name: 'projects', path: '/projects', component: ProjectsScreen},
{name: 'project', path: '/project/:id', component: ProjectScreen, props: true},
]
So the question is how to load the "similar" project image into the carousel when clicking on the <router-link> which outputs the results in the same components.
PS: the other fields are changed, and when getting rid of the carousel it works, so its certain with the setup of the carousel itself with how VueJS deals with routing or something like that..
Move the owl initialization to a lifecycle hook, so it is executed again when the route changes:
<script>
export default {
props: ["id"],
computed: {
...mapGetters(['getProject', 'getSimilars']),
project() {
return this.getProject(this.id)
},
similars() {
return this.getSimilars(this.id)
}
},
mounted() {
$("#owl-work").owlCarousel();
}
}
// Removed from here
</script>
Or, better yet, remove the need for accessing from id and use a ref instead.
<template>
<div>
<div ref="owlwork">
<div class="item" v-for="image in project.images" :key="image.id">
<figure><img :src="'uploads/'+image.url" alt=""></figure>
</div>
</div>
<div class="similars" v-for="similar in similars">
<router-link :key="$route.fullPath" :to="{ name: 'project', params: { id: similar.id, project:similar }}" replace>
<h4>{{similar.title}}</h4>
</router-link>
</div>
</div>
</template>
<script>
export default {
props: ["id"],
computed: {
...mapGetters(['getProject', 'getSimilars']),
project() {
return this.getProject(this.id)
},
similars() {
return this.getSimilars(this.id)
}
},
mounted() {
$(this.$refs.owlwork).owlCarousel();
}
}
// Removed from here
</script>

Categories

Resources