Reputation: 93
I have a carousel which only shows when a user opens my accordion menu.
I am using v-show
to hide the carousel until it's opened. However, it doesn't render the carousel correctly unless I resize the browser window, the first slide shows but to click through the other slides don't show up.
Web inspect shows on load the translate: transform()
empty for slides until the browser window is resized.
I think this is something to do with v-show
and how it renders in the DOM, I have seen people using .$nextTick
or .$watch
to fix similar issues but I don't understand them enough to apply them to my code.
Is there a way for me to solve this issue using v-show with .nextTick()
or .watch()
?
<div :key="project.id">
<!-- project header -->
<li
class="project accordion columns is-flex vcenter"
@click="toggleItem"
style="margin-left: 0; margin-right: 0;"
>
<h3 class="project-title column is-one-quarter">{{ project.Name }}</h3>
<h4 class="project-summary column is-two-thirds">
{{ project.Summary }}
</h4>
<span class="column is-one-half is-gapless project-icon">
<img
src="../static/SVG/Circle.svg"
alt="circle icon for branding & identity"
/>
</span>
</li>
My hidden content is here, it only shows when the accordion is open using v-show
.
<div class="showcase-content columns" v-show="show">
<p class="project-description column is-one-third">
{{ project.Description }}
</p>
<agile :fade="true">
<div
class="image-box column is-two-thirds"
v-for="image in project.image"
:key="image.url"
>
<img :src="buildImageUrl(image.url)" alt="" />
</div>
</agile>
</div>
EDIT I was originally injecting some my accordion in and have refactored the accordion and my code, on the suspicion that injection might not be reactive but the same issue persists. The code on this question is my current code.
<script>
export default {
name: 'ProjectItem',
data: function () {
return {
show: false,
}
},
props: ['project'],
methods: {
toggleItem: function () {
this.show = !this.show
},
buildImageUrl(image) {
if (!image) return '../static/images/SamB.png'
return `http://localhost:1337${image}`
},
},
}
</script>
Upvotes: 3
Views: 644
Reputation: 90013
Note: leaving this part of the answer in as it might be useful for anyone with a similar problem. However, it was added when the question didn't specify the slider library. To read the actual answer, skip to after the separator.
v-show
means the carousel is rendered with display: none
init
-ed and they update on window.resize
Which means your carousel is init
-ing with a height of 0
. And it only readjusts on window.resize
, regardless of the change to the display
property of your v-show
element.
A quick and dirty fix is to trigger window.resize
when the property controlling v-show
changes. I.e:
toggleItem: function () {
this.show = !this.show;
window.dispatchEvent(new Event('resize'));
},
But it's dirty and it might also be expensive, depending on what other components/libraries are listening to resize
.
A much better solution is to call whatever update
method your carousel library exposes when the v-show
condition changes.
If you can't figure out how to do that, I suggest adding the carousel library (with version) to the question.
Another potential problem is if the container of the carousel is animated (i.e: you use some type of transition on it). In which case you need to trigger window.resize
/carousel.update
at the end of the animation, when the container has the correct size. Which is why it would have been ideal to provide a minmal reproducible example.
A(nother) quick and dirty solution for that problem would be to add a transitionend
listener on the animated element and dispatch window.resize
in that callback, in which case you no longer need anything else. Also, I'd actually place a check on the v-show
condition and only dispatch window.resize
when it's true
. But, again, it's not really clean and it could have side-effects.
Edit: since you use vue-agile
, here's your question answered in documentation:
It is also possible to use
v-show
, but you have to use thereload()
method.
Which means this should do it:
<template>
<agile ref="slider" ... />
</template>
...
methods: {
toggleItem: function () {
this.show = !this.show;
this.$nextTick(() => this.$refs.slider.reload());
}
...
}
A working example here.
Note: Replacing v-show
with v-if
is an expensive solution, as it means your carousel is added and removed from DOM each time the condition changes. Each time it will rebuild itself and reload the slider images. That's worse than v-show
+ window.resize
solution.
Note: you might want to have a look at the vue wrapper for Swiper, as (arguably) Swiper is better than Slick.
Upvotes: 3