css3 transtion not working with v-for in vue2 - css

I use transtion in vue2. I added transition in css. Vue template show two box. One way use v-for and array, another way is use variable. btn2 is effective but btn1 not.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div class="item" v-for="item in list" :key="item.index" :style="{marginLeft: item.index + 'px'}">{{ item.value }}</div>
<div class="item" :style="{marginLeft: left + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Heap',
data() {
return {
left: 100,
list: [
{
value: 12,
index: 10
}
]
}
},
methods: {
addone() {
this.list[0]['index']+=10
},
addtwo() {
this.left+=10
}
}
}
</script>

You are using the code :key="item.index" on your first div. Your code then updates that same index.
When a key's value changes, the component it is attached to re-renders. You are not seeing the animation occur because instead of dynamically incrementing the CSS, you are effectively just re-rendering the element with the new CSS.
The purpose of a key is to help Vue keep track of the identity of a given node in a list. It lets Vue know which nodes it can keep and patch up and which ones need to be rendered again.
You should use a static, non-changing value as a key where possible. In the following example I have added an id property to your object and used that as the key.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div
v-for="item in list"
:key="item.id"
class="item"
:style="{marginLeft: item.index.toString() + 'px'}"
>
{{ item.value }}
</div>
<div class="item" :style="{marginLeft: left.toString() + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Example',
data() {
return {
left: 100,
list: [
{
id: '1',
value: 12,
index: 10,
},
],
};
},
methods: {
addone() {
this.list[0].index += 10;
},
addtwo() {
this.left += 10;
},
},
};
</script>

Related

vuedraggable custom style for each individual item

I need to have each vuedraggable item to have different styling in the wrapper tag (for example based on the element's index) like so:
<div class="wrapper">
<div class="grid_item" style="grid-row: 1">
I am Item 1
</div>
<div class="grid_item" style="grid-row: 2">
I am Item 2
</div>
<div class="grid_item" style="grid-row: 3">
I am Item 3
</div>
</div>
I know this is a very simple example that doesn't really need the index but suppose a more complex scenario where it is necessary to have access to the index in the draggable component (not its child template).
Suppose the current component looks like this:
<template>
<div class="wrapper">
<draggable
:list="items"
class="grid_item"
item-key="name">
<template #item="{ element }">
I am {{ element.name }}
</template>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable,
},
data() {
return {
items: [
{
name: 'Item 1'
},
{
name: 'Item 2'
},
{
name: 'Item 3'
},
],
}
},
methods: {
rowForItem(index) {
return `grid-row: ${index + 1}`;
}
},
}
</script>
<style>
.wrapper {
display: grid;
}
.grid_item {
background-color: gray;
}
</style>
How can I make use of the rowForItem method here?

Vue adding dynamic class doesn't change current class

I have wrapper div with padding and I am dynamically adding items inside of it.
I don't want any padding on wrapper div when there is no item in it.
I have created computed method isEmpty to check if there are items or not and used it to add optional class :class={ className: isEmpty } but it doesn't work.
Here is the fiddle link: https://jsfiddle.net/2u9rtdmh/3/
You should wrap an expression for :class into ":
:class="{ className: isEmpty }"
You should read the console errors when trying to diagnose problems, i.e. your jsfiddle doesn't even have an #app element.
The correct syntax for your scenario would be :class="{ 'padding0' : isEmpty }"
Binding HTML Classes
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: "#app",
data: {
},
computed: {
isEmpty() {
return true;
}
}
})
body {
background-color: black;
}
.my-wrapper {
padding: 32px;
background-color: white;
}
.padding0 {
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div :class="{ 'padding0' : isEmpty }" class="my-wrapper">
<div>
<div>
</div>
</div>
</div>
</div>

creating overlapping user icons

I'm trying to create a a component that overlaps over the user icons just as shown in the photo. I've seen this being used on google. If I have a list of user icons, how do I overlap them over each other?
I want something like this .
<template>
<div >
<ul>
<li v-for="user in userList" :key="user.key">
<user-icon-component :name="user.name" :image="user.picture"></user-icon-component>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "UserList",
props: {
userList: {
type: Object,
default: null,
},
},
};
</script>
<style>
</style>
The icon component is just an <img> tag with a user prop:
Vue.component('user-icon-component', {
props: ['user'],
template: `
<img :src="user.picture" width="32" height="32" />
`
})
Give the <li>s position: absolute and the <ul> position: relative to pull them out of the normal document flow. Set the left position on each <li> as a calculation from the index of the loop:
<ul class="icon-container">
<li v-for="(user, key, i) in userList" :key="user.key"
class="icon" :style="{ left: `${i * 20}px` }">
<user-icon-component :user="user"></user-icon-component>
</li>
</ul>
Here's a demo:
Vue.component('user-icon-component', {
props: ['user'],
template: `
<img :src="user.picture" width="32" height="32" />
`
})
/***** APP *****/
new Vue({
el: "#app",
data() {
return {
userList: {
'Bob': { name: 'Bob', key: 1, picture: 'https://www.flaticon.com/svg/static/icons/svg/3084/3084430.svg' },
'Mary': { name: 'Mary', key: 2, picture: 'https://www.flaticon.com/svg/static/icons/svg/3084/3084431.svg' },
'Paul': { name: 'Paul', key: 3, picture: 'https://www.flaticon.com/svg/static/icons/svg/3084/3084452.svg' },
}
}
},
});
.icon-container {
position: relative;
}
.icon {
position: absolute;
}
ul {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul class="icon-container">
<li v-for="(user, key, i) in userList" :key="user.key"
class="icon" :style="{ left: `${i * 20}px` }">
<user-icon-component :user="user"></user-icon-component>
</li>
</ul>
</div>
I liked the idea in your question and took it as a challenge for myself and here is the result:
vue overlapping avatars component
basically the approach I took was to use the component's props as style in style binding. there are some scoped style as well but I think they can be set in the style binding as well if needs be (probably the code could be cleaner).
user prop is an array of objects that contains this property: img: 'imageURL' and using a v-for on a div element with:
:style="{ backgroundImage: `url(${user.img})`}"
we can set the images.
as for the overlapping part, divs have position: relative and using the index of v-for, the style binding becomes like this:
:style="{backgroundImage: `url(${user.img})`, left: `-${i*15}px`}"
which shifts every element to the left by 15px except for the first one.
here is the image of the final result:
Thanks for your question, it was fun :)

Why CSS styles can't be applied to elements created by Buefy?

I'm trying to change the background-color of a table row that was generated by Buefy.
I don't understand why I'm unable to change the color of the table row, but I can change its children color.
I tried both ways, setting the whole class name or just the class name of the element that I want:
.table.is-hoverable tbody tr.detail:not(.is-selected):hover {
background-color: rgb(218, 11, 11) !important;
}
.detail {
background-color: rgb(218, 11, 11) !important;
}
I can, for example change the color of an inner element:
.media {
background-color: yellow !important;
}
Full code
<template>
<section class="hero">
<div class="hero-body">
<div class="container">
<b-table
:data="downloads"
ref="table"
:loading="isLoading"
hoverable
detailed
detail-key="version"
selectable
#select="toggle"
>
<template slot-scope="props">
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="version"
label="Version"
width="70"
>
<span class="tag is-info">{{ props.row.version }}</span>
</b-table-column>
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="download_count"
label="Download Count"
sortable
numeric
>{{ props.row.download_count.toLocaleString() }}</b-table-column>
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="release_date"
label="Release Date"
sortable
centered
>{{props.row.release_date ? new Date(props.row.release_date).toLocaleDateString() : "unknown"}}</b-table-column>
</template>
<template slot="empty">
<section v-if="!isLoading" class="section">
<div class="content has-text-grey has-text-centered">
<p>Looks like it was not possible to load the data.</p>
</div>
</section>
</template>
<template slot="detail" slot-scope="props">
<article class="media">
<div class="media-content">Some text.
<hr>Some other text.
</div>
</article>
</template>
<template slot="footer">
<div v-if="!isLoading" class="has-text-right">This is a footer.</div>
</template>
</b-table>
</div>
</div>
</section>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
downloads: [],
isLoading: true
};
},
mounted() {
this.downloads = [
{ version: "1.5", download_count: 500, release_date: new Date() },
{ version: "1.4", download_count: 400, release_date: new Date() },
{ version: "1.3", download_count: 300, release_date: new Date() },
{ version: "1.2", download_count: 200, release_date: new Date() },
{ version: "1.1", download_count: 100, release_date: new Date() }
];
this.isLoading = false;
},
methods: {
toggle(row) {
this.$refs.table.toggleDetails(row);
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
//This is how I expect it should work.
.table.is-hoverable tbody tr.detail:not(.is-selected):hover {
background-color: rgb(218, 11, 11) !important;
}
//Applying the property to the
.detail {
background-color: rgb(218, 11, 11) !important;
}
.media {
background-color: yellow !important;
}
</style>
Reproduction link:
https://codesandbox.io/s/misty-sea-bmyvc?file=/src/components/HelloWorld.vue
Your CSS is scoped. In the end those classes are assorted with a [data-xxx] selector so they are applied only on this component.
.detail[data-xxx] won't override the lib's CSS, you need just .detail
So put this CSS code into a separated simple <style lang="scss"> and let your component's own CSS in <style lang="scss" scoped>

Vue lazyloading, remove spinner when image loads

I have a vue Nuxt project where I explore lazyloading with lazysizes package.
I created a spinner component (html css only) who should be visible only while the image is loading.
I also created an ImageItem component who includes the spinner component and it looks like this:
< script >
import spinner from '~/components/spinner.vue'
export default {
components: {
spinner,
},
props: {
source: {
type: String,
required: true,
}
},
} <
/script>
<style lang="scss" scoped>.imageItem {
position: relative;
.image {
z-index: 2;
&.lazyload,
&.lazyloading {
opacity: 0;
}
&.lazyloaded {
opacity: 1;
transition: all 1s linear 0.35s;
}
}
}
</style>
<template>
<div class="imageItem">
<spinner />
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
To explain my code, I have props: source where In parent component i pass the image i want to lazyload. Also in the CSS while the image is loading, the image has .lazyloading class and when is loaded .lazyloaded class. Right now when Image is loaded i put it on top of the spinner.
My problem is, when I load the image I want to hide or destroy the spinner element since I think just putting the image on top is not the best way to do it. Can someone give me direction how should I properly hide the spinner when the image is loaded ?
Lazysizes fires an event when loading the image is finished : lazyloaded event, So you can do this :
<template>
<div class="imageItem">
<spinner v-if="lazyloading"/>
<img class="image lazyload" :data-srcset="source" />
</div>
</template>
<script>
import spinner from '~/components/spinner.vue'
export default {
data(){
return {
lazyloading
}
},
mounted(){
document.addEventListener('lazyloaded', (e) => {
this.lazyloading = false;
}
});
}
}
</script>

Resources