VueJS transition on computed values - vue.js

I want to animate a block with posts that can be filtered.
Some filters trigger a computed method filteredPosts and they are assigned to a component liek that <block-article :posts="filteredPosts" />
In my <block-article> component I have something like that :
<template>
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</template>
I want div .posts animate like a translation bottom/fade out on disappear and translation top/ fade in on appear.
I tried that :
<template>
<transition name="slide-fade">
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</transition>
</template>
with corresponding css classes but it doesn't work.
I tried that :
<template>
<div class="posts">
<transition-group name="slide-fade">
<div v-for="post in posts" :key="post.id"></div>
</transition-group>
</div>
</template>
but my class .posts is a grid and here I lost the grid behavior.
THE AIM is to animate the entire div .posts rather than each elements of the v-for.
Any idea ?
Thanks all,

I finally achieve this with :
<transition name="slide-fade">
<div :key="posts.length" class="posts"></div>
</transition>
Nothe the :key="posts.length"
The problem is when posts.length doesn't change but it works in a lots of case. I will search how to fix this exception.

If you animate entire div, you should use transition, but in this case all inner elements not animated. If you want to animate all inner elements. You should use transition-group
In your case I think, need use all this method with build-in tag attribute.
Becouse in dock you can read
https://v2.vuejs.org/v2/guide/transitions.html
Unlike transition, it renders an actual element: a span by default. You can change the element that’s rendered with the tag attribute.
So you can write like this(its not full code, you must add name, key and other attrs)
<transition>
<transition-group tag="div" class="posts">
<div v-for="post in posts"></div>
</transition-group>
</transition>

Related

Using a component inside another component in VueJS

While I was reviewing headlessui's menu component, I saw the use of 2 components that are nested like the following: (see: https://headlessui.dev/vue/menu)
<template>
<Menu>
<MenuButton>More</MenuButton>
<MenuItems>
<MenuItem v-slot="{ active }">
// some content
</MenuItem>
</MenuItems>
</Menue>
</template>
So as you may see, there is a MenuItem component inside of the MenuItems component. And I need something similar to that so I can use a template and put another component's result into that template.
Here the example of what I am trying to do:
<!-- HeadingComponent.vue -->
<div class="group">
<div class="head">
{{ title }} <button>Create New</button>
</div>
<div class="content">
<!-- I want to put some component's rendered content here -->
</div>
</div>
And this is, let's say, a page where I want to use the common component.
<!-- Blog.vue -->
<HeadingComponent :title="Posts">
<BlogPostsComponent :post="someArray"/> <!-- Some other component which may vary -->
</HeadingComponent>
The question
What kind of changes do I need to do in the component HeadingComponent.vue so it works as I expected?
Slots are a good way to add a component to another or even simple html
docs : https://fr.vuejs.org/v2/guide/components-slots.html
<h1>Vue JS Slots Application</h1>
<div id="app">
<slots>
<template slot="slotA"><pre>Slot A Content from parent.</pre></template>
<template><i>Parent Component Content.</i></template>
</slots>
<hr/>
<slots>
<template slot="slotB">Replace Slot B Default Content</template>
<template><b>Replace Default Slot Content.</b></template>
</slots>
</div>
<template id="aside">
<div>
<h3>My Slots App</h3>
<slot>Default Slot Content</slot><br>
<slot name="slotA"></slot><br>
<slot name="slotB"></slot><br>
</div>
</template>
Example of codepen :
https://codepen.io/brian_kim/pen/NpWKGe
Just in a short time, I found something like slots in VueJS which is definitely what I was looking for.
Here is the guide page:
https://v2.vuejs.org/v2/guide/components-slots
What I did in my problem is that I put <slot></slot> tags inside div whose class is content, and then the last sample I gave (Blog.vue) has worked.
<!-- HeadingComponent.vue -->
<div class="group">
<div class="head">
{{ title }} <button>Create New</button>
</div>
<div class="content">
<!-- I want to put some component's rendered content here -->
<slot></slot>
</div>
</div>

Vue Transition inside the div element doesn't work

Could someone explain me, why the following code doesn't work?
<template>
<div class="modal">
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
<transition name="fade-in">
<div #click="close" class="modal__overlay"/>
</transition>
</div>
</template>
I'm trying to create modal with two different animations (slide-in for text area and fade-in for modal overlay).
If i delete the element with class modal and edit code to the following everything works fine.
<template>
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</template>
Referencing Vue.js documentation on transitions
Vue provides a variety of ways to apply transition effects when items are inserted, updated, or removed from the DOM
That means that DOM nodes that transition applies classes to, should be those which are inserted/updated/removed.
Since it is a modal window, I assume that it has v-if directive applied in parent component to handle it's visibility. In order for transition to work, it should wrap DOM element that will be updated.
You can understand it more easily if you move code of your modal window into the parent component. Just for better visualization of elements tree and transition's behavior.
In first example, conditional rendering (v-if) applies to <div class="modal">, which is not wrapped with transition - therefore no animation will be triggered. At the same time, nested nodes are wrapped with transition, but there is nothing that will update or remove them. They are statically displayed and inserted initially on component's creation. Nothing to animate.
In order for it to work as expected following structure would be advisable:
<template>
<transition name="fade-in">
<div
class="modal__overlay"
#click="close"
>
<transition name="slide-in">
<div
v-if="containerVisible"
class="modal__container"
#click.stop
>
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</div>
</transition>
</template>
This solution expects modal__overlay to have position: fixed; style and variable containerVisible to be set to true inside mounted hook of modal component.

preventing Vue from aggresively reusing dom-elements

Condider the following snippet:
<template v-if="tryIsMobile" >
<div class='device device-mobile-portrait' :class="deviceClass">
<div class="device-scroller-container">
<div class='device-scroller'>
<img id='tryit-img-mobile' :src="srcUrlMobile" v-on:load="onImgLoad" v-on:error="onImgError"/>
</div>
</div>
</div>
</template>
<template v-else>
<div class='device device-tablet-landscape' :class="deviceClass" >
<div class="device-scroller-container">
<div class='device-scroller'>
<img id='tryit-img-tablet' :src="srcUrlTablet" v-on:load="onImgLoad" v-on:error="onImgError"/>
</div>
</div>
</div>
</template>
This code conditionally renders one of the two images. Some user action results in the actual shown image to be toggled.
What I'm seeing is the following: When toggling from say, tryit-img-mobile to tryit-img-tablet, the image loaded as part of tryit-img-mobile will get displayed with different dimensions instantly. However, during the time the image loads it's new source :src="srcUrlTablet", the image with src :src="srcUrlMobile" still displays.
This is probably due to Vue using the same img-tag for both the templates. How can I prevent Vue from doing this, and instead use seperate img-tags?
In cases such as this, Vue uses a special key attribute that tells it not to reuse the same element. Give each element this attribute with a unique value, and Vue will no longer reuse the same element:
<div v-if="tryIsMobile"
class="device device-mobile-portrait"
:class="deviceClass"
key="mobile"
>
...
</div>
<div v-else
class="device device-tablet-landscape"
:class="deviceClass"
key="tablet"
>
...
</div>

Some part of Vue2 component's template does not render

I have a vue2 app (called 'renderer-app') with a component as its child (called 'section-renderer'), which in turn has a child component. In the parent component (section-renderer) I have a template option in which the multiple components are added to the parent component:
template:
<div>
<component v-for="(item, index) in layoutArray" :is="item.template" :cardObj="item.cardObj" :key="item.id"></component>
<!--the above component gets rendered as it should, but the rest of the template is not rendered-->
<div class="parallax-container">
<div class="parallax">
<img src="./images/banner2.jpg">
</div>
<div class="advertisement-text-box">
<p class="advertisement-header">some header</p>
<p class="advertisement-text">some text</p>
</div>
</div>
</div>
As I have shown above in the comment line, the child component gets rendered multiple times as it should, but the rest of the template which should be rendered once per parent component does not render.
Strangely enough, if I insert a div with text in it between the two, the rest of the template renders too. So this works (of course with an unwanted line):
template:
<div>
<component v-for="(item, index) in layoutArray" :is="item.template" :cardObj="item.cardObj" :key="item.id"></component>
<div>extra</div> <!--the extra div with text-->
<div class="parallax-container">
<div class="parallax"><img src="./images/banner2.jpg"></div>
<div class="advertisement-text-box">
<p class="advertisement-header">some header</p>
<p class="advertisement-text">some text</p>
</div>
</div>
</div>
I tried various positioning of the div around the child component and the rest of the template, but none seemed to work. Any help would be appreciated.

Vue named slots do not work when wrapped

I have a responsive-menu component which I want to use named slots inside of this up my template markup:
<template>
<div class="responsive-menu">
<div class="menu-header">
<slot name="header"></slot>
</div>
</div>
</template>
Whenever I try my named slot like this it work perfectly fine:
<responsive-menu>
<h3 slot="header">Responsive menu header</h3>
</responsive-menu>
However as soon as I wrap it with a class nothing shows up anymore.
<responsive-menu>
<div class="container">
<h3 slot="header">Responsive menu header</h3>
</div>
</responsive-menu>
What is going on here? Shouldn't I just be able to wrap the named component? Which does it appear that my named slots need to be direct children of my Vue component?
It does not work because your "wrapped slot" isn't direct child of responsive-menu tag.
try something like that:
<responsive-menu>
<div class="container" slot="header">
<h3>Responsive menu header</h3>
</div>
</responsive-menu>
jsfiddle
It works with Vue >= 2.6. Just upgrade vue and vue-template-compiler.