Vue v-bind:style not updating with nested data changes - vue.js

The component's style doesn't update with the itemStyleMap[index] value chage:
<view
class="item"
v-for="(item, index) in itemList"
:key="index"
:style="itemStyleMap[index]"
/>
I also tried:
<view
class="item"
v-for="(item, index) in itemList"
:key="index"
:style="{
background: itemStyleMap[index]['background'],
display: itemStyleMap[index]['display'],
zIndex: itemStyleMap[index]['zIndex'],
transform: itemStyleMap[index]['transform'],
transformOrigin: itemStyleMap[index]['transformOrigin'],
}"
/>
The itemStyleMap in data is something like this:
{
1: {
background: 'unset',
display: 'none',
zIndex: 'unset',
transform: 'unset',
transformOrigin: 'unset',
},
2: {
background: 'unset',
display: 'none',
zIndex: 'unset',
transform: 'unset',
transformOrigin: 'unset',
},
3: {
background: 'unset',
display: 'none',
zIndex: 'unset',
transform: 'unset',
transformOrigin: 'unset',
},
}
A more simple demo can be find here: https://jsfiddle.net/wfx6dhy5/7/
Is there any other better way to control infinate amount of style sets like this?

I don't know what is wrong in your project but I did something like this and it works.
check this out.
https://jsfiddle.net/softvini/c4j8ou9r/4/
<div id="app">
<div v-for="item of itemList" :key="item" :style="itemStyleMap[item]">
{{item}}
</div>
</div>
var vm = new Vue({
el: "#app",
data() {
return {
itemList: [0, 1, 2],
itemStyleMap: [{
background: "purple",
display: "block",
zIndex: "unset",
transform: "unset",
transformOrigin: "unset",
},
{
background: "red",
display: "block",
zIndex: "unset",
transform: "unset",
transformOrigin: "unset",
},
{
background: "brown",
display: "block",
zIndex: "unset",
transform: "unset",
transformOrigin: "unset",
},
],
};
},
})
Maybe you need to read more about reactivity. So you can read it here:
https://v2.vuejs.org/v2/guide/reactivity.html

in your https://jsfiddle.net/wfx6dhy5/7/ data should be a function.
el: "#app",
data(){
return {
spanStyle: {
t:{
fontSize: "36px",
color: 'yellow',
border: null
}
}
}
},
methods: {
changeColors() {
this.spanStyle.color = (this.spanStyle.t.color == 'red') ? 'blue' : 'red';
this.spanStyl.border = (this.spanStyle.t.border == '3px solid blue') ? '3px solid red' : '3px solid blue';
}
}
});
Also, you missed .t in changeColors method. this.spanStyle.color should be this.spanStyle.t.color and this.spanStyl.border should be this.spanStyl.t.border

Related

Display after class based on amount of items in loop

I am displaying data in html using in loop, inside it I got a class
div(v-for="item in items" :key="item.id")
.circle
p {{ item.name }}
in my css I got
.circle::after
content: ''
background: grey
height: 5px
width: 130px
position: absolute
left: 0
top: 16px
z-index: -1
Can I display this after class only if there is more than 1 item in my loop?
As I said in the comments, ::after pseudo element is CSS construct so you cannot control it using Vue.
I see two possible solutions:
Define two classes - one with ::after and one without it and conditionally apply only one of the classes based on the number of items
You can actually do it entirely with CSS using :not(:only-child) pseude-selector. See example below
new Vue({
el: "#app",
data: {
toggle: true,
items: [
{ text: "Learn JavaScript", id: 1 },
{ text: "Learn Vue", id: 2 },
{ text: "Play around in JSFiddle", id: 3 },
{ text: "Build something awesome", id: 4 }
]
},
computed: {
computedItems() {
return this.toggle ? this.items : this.items.slice(0,1)
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.circle:not(:only-child)::after {
content: " ➥";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="toggle = !toggle">
Switch items
</button>
<div>
<div v-for="item in computedItems" :key="item.id" class="circle">
{{ item.text }}
</div>
</div>
</div>

Why does transition-group animate opacity but not animate height?

the code below animates the element you click on, but I want it to smoothly decrease the height to zero as well. Unfortunately, the height does not change, but everything works with opacity.
height: 0 !improtant; //does not help to solve the problem
<template>
<transition-group name="msgAnimation" tag="div">
<div v-for="(obj, i) in messages" :key="obj.key" class="wrapper">
<div class="wrapper__block" #click="messages.splice(i, 1)">
{{ obj.msg }}
</div>
</div>
</transition-group>
</template>
<style lang="css">
.msgAnimation-enter-active,
.msgAnimation-leave-active {
transition: all 5s;
}
.msgAnimation-enter,
.msgAnimation-leave-to {
height: 0;
opacity: 0;
}
.wrapper {
width: 100%;
height: 9vmin;
}
.wrapper__block {
background: green;
height: 9vmin;
width: 100%;
}
</style>
<script>
export default {
name: "HelloWorld",
data() {
return {
totalAmount: 0,
messages: [{ key: 0, msg: "Are u hacker" }],
};
},
};
</script>
Your CSS rules was declared after your animation
const example = {
data() {
return {
totalAmount: 0,
messages: [{
key: 0,
msg: "Are u hacker"
},
{
key: 1,
msg: "Are u hacker"
}
],
};
},
};
const app = new Vue(example);
app.$mount("#app");
.msgAnimation-enter-active,
.msgAnimation-leave-active {
transition: all 5s;
}
.wrapper {
width: 100%;
height: 9vmin;
overflow:hidden;
}
.wrapper__block {
background: green;
width: 100%;
}
.msgAnimation-enter,
.msgAnimation-leave-to {
opacity: 0;
height: 0;
}
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app">
<transition-group name="msgAnimation" tag="div">
<div v-for="(obj, i) in messages" :key="obj.key" class="wrapper">
<div class="wrapper__block" #click="messages.splice(i, 1)">
{{ obj.msg }}
</div>
</div>
</transition-group>
</div>
You're not limiting overflow of .wrapper, and you have a height specified in .wrapper__block. Thus, even if .wrapper goes smoothly to 0, its child will not.
Setting height: 100%; on .wrapper__block, or setting overflow: hidden on .wrapper should do the trick.
<template>
<transition-group name="msgAnimation" tag="div">
<div v-for="(obj, i) in messages" :key="obj.key" class="wrapper">
<div class="wrapper__block" #click="messages.splice(i, 1)">
{{ obj.msg }}
</div>
</div>
</transition-group>
</template>
<style lang="css">
.msgAnimation-enter-active,
.msgAnimation-leave-active {
transition: all 5s;
}
.msgAnimation-enter,
.msgAnimation-leave-to {
height: 0;
opacity: 0;
}
.wrapper {
width: 100%;
height: 9vmin;
}
.wrapper__block {
background: green;
height: 100%;
width: 100%;
}
</style>
<script>
export default {
name: "HelloWorld",
data() {
return {
totalAmount: 0,
messages: [{ key: 0, msg: "Are u hacker" }],
};
},
};
</script>
Alternatively, if you don't mind distortion during the animation, it's a lot more performant to animate transform: scaleY(0), as transform and opacity are applied at the Composition step in CSS, you prevent a lot of in-between style calculations, making your app noticeably faster whn you have several thousand messages.

How do I switch between a template using a Grid Layout and another using Flex depending on what is supported by the browser dynamically?

I have the following code CODEPEN
HTML
body
#__nuxt
#__layout
//- CSS Grid
.news-item(:key="item.feed_item_id" :ref="item.feed_item_id" role="listitem" tabindex="0")
.news-item-pubdate 1m
.news-item-title(:style="titleStyle") {{item.title}}
span.news-item-link example.com
.news-item-likes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count {{item.likes}}
.news-item-dislikes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count {{item.dislikes}}
.news-item-bullish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count {{item.bullish}}
.news-item-bearish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count {{item.bearish}}
.news-item-comments
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count 0
.news-item-tags.has-text-right(:style="tagStyle")
.news-item-tag(v-for='(tag, i) in item.tags' :key='item.tag' #click.stop="updateTag(tag)")
a.button.is-paddingless.is-small.is-uppercase.is-text
|{{tag}}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
//- Flexbox
.news-item2(:key="item.feed_item_id" :ref="item.feed_item_id" role="listitem" tabindex="0")
.news-item-pubdate-wrapper
.news-item-pubdate2 1m
.news-item-content-wrapper
.news-item-title-wrapper
.news-item-title2(:style="titleStyle") {{item.title}}
span.news-item-link2 example.com
.news-item-votes-wrapper
.news-item-likes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count2 {{item.likes}}
.news-item-dislikes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count2 {{item.dislikes}}
.news-item-bullish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count2 {{item.bullish}}
.news-item-bearish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count2 {{item.bearish}}
.news-item-comments2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count2 0
.news-item-tags-wrapper
.news-item-tags2.has-text-right(:style="tagStyle")
.news-item-tag2(v-for='(tag, i) in item.tags' :key='item.tag' #click.stop="updateTag(tag)")
a.button.is-paddingless.is-small.is-uppercase.is-text
|{{tag}}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
CSS
/**
Using CSS GRID
*/
:root {
--border-color: lightgray;
}
$grey: hsl(0, 0%, 48%);
$grey-light: hsl(0, 0%, 71%);
$size-7: 0.75rem;
$warning-light: hsl(48, 100%, 67%);
.news-item {
display: grid;
grid-template-areas:
'time title title title title title title tags'
'time likes dislikes bullish bearish comments . tags';
grid-template-rows: 1fr auto;
grid-template-columns: auto auto auto auto auto auto 1fr auto;
border-bottom: 1px solid var(--border-color);
}
.news-item-title {
grid-area: title;
overflow: hidden;
}
.news-item-likes {
grid-area: likes;
}
.news-item-dislikes {
grid-area: dislikes;
}
.news-item-bullish {
grid-area: bullish;
}
.news-item-bearish {
grid-area: bearish;
}
.news-item-comments {
grid-area: comments;
}
.news-item-tags {
grid-area: tags;
overflow: hidden;
}
a.news-item-tag {
color: $grey;
text-decoration: none;
}
.news-item-link {
color: $grey;
font-size: $size-7;
}
.news-item-pubdate {
grid-area: time;
align-self: center;
color: $grey-light;
font-size: $size-7;
}
.news-item-vote-icon {
color: $grey-light;
margin-right: 0.1rem;
}
.news-item-vote-count {
font-size: 0.75rem;
margin-right: 0.5rem;
color: $grey-light;
}
.news-item-selected {
background: $warning-light;
}
/** https://stackoverflow.com/questions/23608346/how-to-style-a-div-like-the-button-element */
.news-item {
cursor: pointer;
user-select: none;
}
a.button.is-paddingless.is-small.is-uppercase.is-text {
text-decoration: none;
line-height: 1.5;
color: $grey;
}
/**
Using FLEXBOX
*/
.news-item2 {
display: flex;
align-items: center;
border-bottom: 1px solid var(--border-color);
}
.news-item-content-wrapper {
flex: 1;
display: flex;
flex-direction: column;
}
.news-item-votes-wrapper {
display: flex;
}
.news-item-title2 {
overflow: hidden;
}
.news-item-link2 {
color: $grey;
font-size: $size-7;
}
.news-item-pubdate2 {
grid-area: time;
align-self: center;
color: $grey-light;
font-size: $size-7;
}
.news-item-vote-icon2 {
color: $grey-light;
margin-right: 0.1rem;
}
.news-item-vote-count2 {
font-size: 0.75rem;
margin-right: 0.5rem;
color: $grey-light;
}
.news-item-tags2 {
overflow: hidden;
}
Vue JS
new Vue({
el: "#__nuxt",
data() {
return {
item: {
feed_item_id: 1,
title: 'The first one is made with a grid layout while the second one is made with a flex layout. The templates are different, how do I load the right one based on what is supported by the browser?',
tags: ['trump', 'putin', 'defi'],
likes: 10,
dislikes: 5,
bullish: 6,
bearish: 0,
},
lineCountTag: 2,
lineCountTitle: 2,
heightPerLineTag: 30,
heightPerLineTitle: 24,
};
},
computed: {
tagStyle() {
return {
height: this.heightPerLineTag * this.lineCountTag + "px",
maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
};
},
titleStyle() {
return {
height: this.heightPerLineTitle * this.lineCountTitle + "px",
maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
};
}
}
});
I have made 2 rows, first row is made with grid layout
second one uses flexbox layout
I want a Vue component which basically can load one of them depending on whether the browser supports flexbox or grid
The problem is the templates are different from each other
How do I do this?
I think I got it, need 2 make 2 separate components, one with a flex and one with the css grid as the structure, cannot have both in one component because the structural layout of the item is different using css grid and flexbox, make a 3rd component that checks for support of CSS Grid in JS using a computed property CSS.supports('display', 'grid')
HTML
script#flex(type="text/x-template")
.news-item2(
:key="item.feed_item_id",
:ref="item.feed_item_id",
role="listitem",
tabindex="0"
)
.news-item-pubdate-wrapper
.news-item-pubdate2 1m
.news-item-content-wrapper
.news-item-title-wrapper
.news-item-title2(:style="titleStyle") {{ item.title }}
span.news-item-link2 example.com
.news-item-votes-wrapper
.news-item-likes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count2 {{ item.likes }}
.news-item-dislikes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count2 {{ item.dislikes }}
.news-item-bullish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count2 {{ item.bullish }}
.news-item-bearish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count2 {{ item.bearish }}
.news-item-comments2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count2 0
.news-item-tags-wrapper
.news-item-tags2.has-text-right(:style="tagStyle")
.news-item-tag2(
v-for="(tag, i) in item.tags",
:key="item.tag",
#click.stop="updateTag(tag)"
)
a.button.is-paddingless.is-small.is-uppercase.is-text
| {{ tag }}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
script#grid(type="text/x-template")
.news-item(
:key="item.feed_item_id",
:ref="item.feed_item_id",
role="listitem",
tabindex="0"
)
.news-item-pubdate 1m
.news-item-title(:style="titleStyle") {{ item.title }}
span.news-item-link example.com
.news-item-likes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count {{ item.likes }}
.news-item-dislikes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count {{ item.dislikes }}
.news-item-bullish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count {{ item.bullish }}
.news-item-bearish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count {{ item.bearish }}
.news-item-comments
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count 0
.news-item-tags.has-text-right(:style="tagStyle")
.news-item-tag(
v-for="(tag, i) in item.tags",
:key="item.tag",
#click.stop="updateTag(tag)"
)
a.button.is-paddingless.is-small.is-uppercase.is-text
| {{ tag }}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
script#item(type="text/x-template")
template(v-if="supportsCssGrid", :item="item")
grid(:item="item")
template(v-else)
flex(:item="item")
body
#__nuxt
#__layout
item(:item="item")
Vue JS
const lineClamp = {
data() {
return {
lineCountTag: 2,
lineCountTitle: 2,
heightPerLineTag: 30,
heightPerLineTitle: 24
};
},
computed: {
tagStyle() {
return {
height: this.heightPerLineTag * this.lineCountTag + "px",
maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
};
},
titleStyle() {
return {
height: this.heightPerLineTitle * this.lineCountTitle + "px",
maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
};
}
}
};
Vue.component("flex", {
template: "#flex",
mixins: [lineClamp],
props: {
item: {
type: Object,
default: () => {},
required: true
}
}
});
Vue.component("grid", {
template: "#grid",
mixins: [lineClamp],
props: {
item: {
type: Object,
default: () => {},
required: true
}
}
});
Vue.component("item", {
template: "#item",
props: {
item: {
type: Object,
default: () => {},
required: true
}
},
computed: {
supportsCssGrid() {
return CSS.supports("display", "grid");
}
}
});
new Vue({
el: "#__nuxt",
data() {
return {
item: {
feed_item_id: 1,
title:
"The first one is made with a grid layout while the second one is made with a flex layout. The templates are different, how do I load the right one based on what is supported by the browser?",
tags: ["trump", "putin", "defi"],
likes: 10,
dislikes: 5,
bullish: 6,
bearish: 0
},
lineCountTag: 2,
lineCountTitle: 2,
heightPerLineTag: 30,
heightPerLineTitle: 24
};
},
computed: {
tagStyle() {
return {
height: this.heightPerLineTag * this.lineCountTag + "px",
maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
};
},
titleStyle() {
return {
height: this.heightPerLineTitle * this.lineCountTitle + "px",
maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
};
}
}
});

How can I modify a value in a array with a click method ? [VueJS]

I just started vuejs today. I got vue "example1" which contain as data, a variable "items". This variable contains an array "deck". This array contains multiple character stats (team, weapon, position...).
I don't know how to figure this out, if you have any solution or any direction where I can find my anwser.
I want on click on the character, to modify the gridColumn position, which is binded by "x". They are displayed on a 9*12 grid.
Thanks a lot.
html, body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#board {
display: grid;
grid-template-columns: repeat(12, 80px);
grid-template-rows: repeat(9, 80px);
grid-gap: 10px;
}
#board .card {
background-color: pink;
border: 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<section id="board">
<div class="card" v-for="item in items" v-bind:style="{backgroundColor:item.bg,gridColumn:item.x,gridRow:item.y}" v-on:click="move">
{{ item.clan }}
<br>
{{ item.arme }}
<br>
{{ item.force }}
</div>
</section>
<script>
var deck = [
//natif
{
clan: 'natif',
arme:'filet',
force: 1,
bg: 'green',
x: 2,
y: 6
},
{
clan: 'natif',
arme:'filet',
force: 2,
bg: 'green',
x: 3,
y: 6
}
//etc
];
var example1 = new Vue({
el: '#board',
data: {
items: deck
},
methods: {
move: function () {
// increase "x" value of the clicked item.
}
}
});
</script>
You can pass the index of the item clicked and modify it inside the function by referencing the index.
html, body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#board {
display: grid;
grid-template-columns: repeat(12, 80px);
grid-template-rows: repeat(9, 80px);
grid-gap: 10px;
}
#board .card {
background-color: pink;
border: 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<section id="board">
<div class="card" v-for="(item,index) in items" v-bind:style="{backgroundColor:item.bg,gridColumn:item.x,gridRow:item.y}" v-on:click="move(index)">
{{ item.clan }}
<br>
{{ item.arme }}
<br>
{{ item.force }}
</div>
</section>
<script>
var deck = [
//natif
{
clan: 'natif',
arme:'filet',
force: 1,
bg: 'green',
x: 2,
y: 6
},
{
clan: 'natif',
arme:'filet',
force: 2,
bg: 'green',
x: 3,
y: 6
}
//etc
];
var example1 = new Vue({
el: '#board',
data: {
items: deck
},
methods: {
move: function (i) {
this.items = this.items.map((item,indx) => {
if(indx === i)
return ({...item,x: item.x + 1});
return item;
}) //modify logic accordingly
}
}
});
</script>
This is how you do it according to the OFFICIAL VUE.JS DOCUMENTATION
[Mutating the array with Vue.set method]:
var deck = [
//natif
{
clan: 'natif',
arme: 'filet',
force: 1,
bg: 'green',
x: 2,
y: 6
},
{
clan: 'natif',
arme: 'filet',
force: 2,
bg: 'green',
x: 3,
y: 6
}
//etc
];
var example1 = new Vue({
el: '#board',
data: {
items: deck
},
methods: {
move: function(index) {
/* creating a REFERENCE of the clicked item's object */
let modifiedObject = this.items[index];
/* increaseing "x" value of temporary REFERENCE */
modifiedObject.x++;
/* Mutating the items array WITHOUT LOSING REACTIVITY by replacing the existing object with local modified object */
Vue.set(this.items, index, modifiedObject);
}
}
});
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#board {
border: 1px solid black;
display: grid;
grid-template-columns: repeat(12, 40px);
grid-template-rows: repeat(9, 40px);
grid-gap: 10px;
}
#board .card {
background-color: pink;
border: 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<section id="board">
<div class="card" v-for="(item,index) in items" v-bind:style="{backgroundColor:item.bg,gridColumn:item.x,gridRow:item.y}" v-on:click="move(index)">
{{ item.clan }}
<br> {{ item.arme }}
<br> {{ item.force }}
</div>
</section>

Vuejs - List transition via transition-group - parent container not animating smoothly

So I have a list that I am rendering with v-for, and I am also using transition-group to animate adding and removing items from this list. My problem is that while I can animate the adding/removing of list items, the container surrounding my entire list isn't smoothly transitioning its height. I'm wondering how I can fix this.
Here is an example with a 'Run code snippet' at the end:
var vm = new Vue({
el: '#vue-instance',
data: {
inventory: [
{name: 'Air', price: 1000, id:"name0"},
{name: 'Pro', price: 1800, id:"name1"},
{name: 'W530', price: 1400, id:"name2"},
{name: 'One', price: 300, id:"name3"}
]
},
methods: {
addItem() {
this.inventory.push({
name: 'Acer',
price: 700,
id: 'name4'
});
},
removeItem(index) {
this.inventory.splice(index, 1);
this.inventory.forEach((item, index) => {
item.id = `name${index}`;
});
}
}
});
.container {
background-color: green;
}
.list-enter {
opacity: 0;
}
.list-enter-active {
transition: all 2s;
height: 100%;
animation: slide-in 2s ease-out forwards;
}
.list-leave-to {
}
.list-leave-active{
transition: all 2s;
opacity: 0;
animation: slide-out 2s ease-out forwards;
}
.list-move {
transition: transform 2s;
}
#keyframes slide-in {
from {
transform: translateY(-100px);
}
to {
transform: translateY(0);
}
}
#keyframes slide-out {
from {
transform: translateY(0);
}
to {
transform: translateX(30px);
}
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="vue-instance">
<div class="container">
<ul>
<transition-group name="list">
<div v-for="(item, index) in inventory" :key="item.name">
<label :for="item.id">Hello</label>
<input :id="item.id">
<button #click="removeItem(index)">
Remove item
</button>
<button #click="addItem">
Add item
</button>
</div>
</transition-group>
</ul>
</div>
</div>
It can be done by adding a height value to the slide out animation
#keyframes slide-out {
from {
transform: translateY(0);
height: 10px;
}
to {
transform: translateY(-30px);
height: 0;
}
}
https://codepen.io/jacobgoh101/pen/EEvNzB?editors=0100