vue dynamic vlaue based on multiple data - vue.js

based on most of vue docs that have mentioned to single parameter,
I used the v-on:mouseover and leave to control style dynamically based on each item color because i need to change each item color by hover based on its color and although I used !important with styles, it doesn't change
<li v-for="item in items" v-bind:key="item.id">
<a class="my-link"
v-on:mouseleave="mouseLeave(item)"
v-on:mouseover="mouseOver(item)">
{{ item.title }}
</a>
</li>
data() {
return {
items: [
{
id: 1,
title: "one",
color: "#ccc"
},
{
id: 2,
title: "two",
color: "#000"
},
{
id: 3,
title: "three",
color: "#c7c7c7"
}
]
}
},
methods: {
mouseOver: function(item){
this.$el.children[0].style.color = 'red !important';
},
mouseLeave: function(item){
this.$el.children[0].style.color = `${item.color} !important`;
}
}

Another approach without using mouseleave and mouseover, only CSS:
Apply the main color with :style for each list item from its data definition. Also add class on the parent element class="list" with the color for hover effect. And finally class="list-item" which inherits color from the parent on hover only. Thus color red is inherit on hover only:
<li v-for="item in items" v-bind:key="item.id" class="list" :style="{ color: item.color }">
<a class="list-item">
{{ item.title }}
</a>
</li>
<style scopped>
.list-item {
color: red;
}
.list-item:hover {
color: inherit !important;
}
</style>
Live example:
new Vue({
el: '#app',
data: {
items: [
{
id: 1,
title: "one",
color: "red",
},
{
id: 2,
title: "two",
color: "green",
},
{
id: 3,
title: "three",
color: "blue",
}
]},
template: `
<div>
<li v-for="item in items" v-bind:key="item.id" class="list" :style="{ color: item.color }">
<a class="my-link list-item">
{{ item.title }}
</a>
</li>
</div>`
})
.list-item {
color: #ccc;
}
.list-item:hover {
color: inherit !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">

Related

nuxtjs add and remove class on click on elements

I am new in vue and nuxt and here is my code I need to update
<template>
<div class="dashContent">
<div class="dashContent_item dashContent_item--active">
<p class="dashContent_text">123</p>
</div>
<div class="dashContent_item">
<p class="dashContent_text">456</p>
</div>
<div class="dashContent_item">
<p class="dashContent_text">789</p>
</div>
</div>
</template>
<style lang="scss">
.dashContent {
&_item {
display: flex;
align-items: center;
}
&_text {
color: #8e8f93;
font-size: 14px;
}
}
.dashContent_item--active {
.dashContent_text{
color:#fff;
font-size: 14px;
}
}
</style>
I tried something like this:
<div #click="onClick">
methods: {
onClick () {
document.body.classList.toggle('dashContent_item--active');
},
},
but it changed all elements and I need style change only on element I clicked and remove when click on another
also this code add active class to body not to element I clicked
This is how to get a togglable list of fruits, with a specific class tied to each one of them.
<template>
<section>
<div v-for="(fruit, index) in fruits" :key="fruit.id" #click="toggleEat(index)">
<span :class="{ 'was-eaten': fruit.eaten }">{{ fruit.name }}</span>
</div>
</section>
</template>
<script>
export default {
name: 'ToggleFruits',
data() {
return {
fruits: [
{ id: 1, name: 'banana', eaten: false },
{ id: 2, name: 'apple', eaten: true },
{ id: 3, name: 'watermelon', eaten: false },
],
}
},
methods: {
toggleEat(clickedFruitIndex) {
this.fruits = this.fruits.map((fruit) => ({
...fruit,
eaten: false,
}))
return this.$set(this.fruits, clickedFruitIndex, {
...this.fruits[clickedFruitIndex],
eaten: true,
})
},
},
}
</script>
<style scoped>
.was-eaten {
color: hsl(24, 81.7%, 49.2%);
}
</style>
In Vue2, we need to use this.$set otherwise, the changed element in a specific position of the array will not be detected. More info available in the official documentation.

Vue Element UI - html inside <el-select>

I would like to implement a select element with different colored-labels for each entry:
My code looks like this:
var Main = {
data() {
return {
selectedState: null,
processStates: [
{
value: 0,
label: 'New',
color: 'ffffff'
},
{
value: 1,
label: 'Ready',
color: 'ff9933'
},
{
value: 2,
label: 'Running',
color: '008000'
},
{
value: 3,
label: 'Rejected',
color: 'cc0000'
},
{
value: 4,
label: 'Terminated',
color: '2E9AFE'
}
]
}
},
methods: {}
}
var Ctor = Vue.extend(Main);
new Ctor().$mount('#app');
#import url("//unpkg.com/element-ui#2.4.4/lib/theme-chalk/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.4.11/lib/index.js"></script>
<div id="app">
<el-select v-model="selectedState" style="width:200px">
<el-option
v-for="state in processStates"
:key="state.value"
:label="state.label"
:value="state.value"
>
<span>
<el-tag :style="'background-color:#' + state.color"> </el-tag> {{ state.label }}
</span>
</el-option>
</el-select>
</div>
AS you can see, I managed to inject html into the option tag and have the desired result.
However, I would like to have the same html when one option is selected.
Desired result:
Any idea how can I achieve it?
You have to use the prefix slot for this. As done below, also I changed the selectedState to an object, but you can also still use the string value, but then you have to do a lookup to get the color
var Main = {
data() {
return {
selectedState: { color: 'ffffff'},
processStates: [
{
value: 0,
label: 'New',
color: 'ffffff'
},
{
value: 1,
label: 'Ready',
color: 'ff9933'
},
{
value: 2,
label: 'Running',
color: '008000'
},
{
value: 3,
label: 'Rejected',
color: 'cc0000'
},
{
value: 4,
label: 'Terminated',
color: '2E9AFE'
}
]
}
},
methods: {}
}
var Ctor = Vue.extend(Main);
new Ctor().$mount('#app');
#import url("//unpkg.com/element-ui#2.4.4/lib/theme-chalk/index.css");
.el-input--prefix .el-input__inner {
padding-left: 40px;
}
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.4.11/lib/index.js"></script>
<div id="app">
<el-select v-model="selectedState" value-key="value" style="width:200px">
<template slot="prefix">
<el-tag class="prefix" :style="`background-color: #${selectedState.color}`"/>
</template>
<el-option
v-for="state in processStates"
:key="state.value"
:label="state.label"
:value="state"
>
<span>
<el-tag :style="'background-color:#' + state.color"> </el-tag> {{ state.label }}
</span>
</el-option>
</el-select>
</div>

v-for: Array element and destructuring of properties

When working with an array of objects, is it possible in a v-for-loop to assign the current object to a variable and destruct its properties at the same time? Something like this:
<div v-for="(person = {name, age}, index) in persons">
Ultimately I'm looking for a way to use e.g. both the whole person object and its properties in a template.
As far as I know, you can't do both.
But, you can destructure, i.e.
<div v-for="({name, age}, index) in persons">
You could then just access the correct element with the index: persons[index].
Example:
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript",
done: false
},
{
text: "Learn Vue",
done: false
},
{
text: "Play around in JSFiddle",
done: true
},
{
text: "Build something awesome",
done: true
}
]
},
methods: {
toggle: function(index) {
this.todos[index].done = !this.todos[index].done
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="({text, done}, i) in todos">
<label>
<input type="checkbox"
v-on:change="toggle(i)"
v-bind:checked="done">
<del v-if="done">
{{ text }}
</del>
<span v-else>
{{ text }}
</span>
{{todos[i]}}
</label>
</li>
</ol>
</div>

Is it impossible to update data without update a component in Vue.js?

There is a template
<div class="wrap">
<p v-for="item in items">
{{ item }}
</p>
</div>
There is a data
data: {
items: [1, 2, 3]
}
Problem: after update DOM I trying to actualization data, but update a data causes to re-rendering a component.
Expected result:
Question: how to synchronisation data after update DOM?
Live demo
Below is one simple demo on the implemention of drag/drop by Vue.
Create one watch, it will watch this.dragedItem. if changed, it will re-calculate the styles which apply to the dragItems.
When drag start, get current dragging item (dragedItem=current item).
When drag end, reset dragging item (dragedItem={}),
When drop, remove item from dragItems, then push to dropItems.
In above steps, we just need to change the data, then Vue will auto render.
You can check HTML Drag And Drop API for more details.
For the transition effects, you can check Vue Guide: Enter/Leave Transition and Vue Guide: State Transtion.
app = new Vue({
el: "#app",
data: {
dragItems: ['A', 'B', 'C', 'D'],
dragedItem: {},
dropItems: [],
styles: {},
defaultStyle: {'opacity': '', 'background-color': 'yellow'}
},
watch: {
dragedItem: function () {
this.styles = this.dragItems.reduce((pre, cur) => {
pre[cur] = cur === this.dragedItem.item ? {'opacity': 0.5, 'background-color': 'blue'} : {'opacity': '', 'background-color': 'yellow'}
return pre
}, {})
}
},
methods: {
onDragStart: function (ev, item, index) {
ev.dataTransfer.setData('text/plain',null)
this.dragedItem = {item, index}
},
onDragEnd: function (ev) {
this.dragedItem = {}
},
onDrop: function (ev) {
this.dropItems.push(this.dragedItem.item)
this.dragItems.splice(this.dragedItem.index, 1)
}
}
})
.dragzone {
display:flex;
flex-direction: row;
}
.dragger {
width: 30px;
height: 30px;
text-align: center;
border: 1px solid gray;
}
.dropzone {
width: 200px;
height: 30px;
background: green;
padding: 10px;
display:flex;
flex-direction: row;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<div class="dragzone">
<div class="dragger" draggable="true"
v-for="(item, index) in dragItems" :key="index"
:style="styles[item] ? styles[item] : defaultStyle"
#dragstart="onDragStart($event, item, index)"
#dragend="onDragEnd($event)"
>
{{item}}
</div>
</div>
<div class="dropzone" #drop.prevent="onDrop($event)"
#dragover.prevent=""
>
<div class="dragger" style="background-color:yellow" draggable="true" v-for="(item, index) in dropItems" :key="index">
{{item}}
</div>
</div>
</div>

Toggle form inside v-for using vue.js

How can i toggle form inside v-for loop,I have a form inside v-for which i want to display (toggle) on click.
But when i click all the form inside the v-for gets toggled.
Secondly is it better approach to keep the form inside the loop,when have large amount of data inside loop or load it as a separate component.
This is what i am trying to do.
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
],
show: ''
},
methods: {
toggle: function(todo) {
this.show = !this.show
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="show">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>
There is only 1 reactive variable show. Setting it to true while all form is using v-if="show", will show everything.
You can set show to something that each form uniquely have. For example, its text, and perform a v-if using its text.
demo: https://jsfiddle.net/jacobgoh101/umaszo9c/
change v-if="show" to v-if="show === todo.text"
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="show === todo.text">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>
change toggle method
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
],
show: ''
},
methods: {
toggle: function(todo) {
if (this.show === todo.text)
this.show = false
else
this.show = todo.text
}
}
})
property "show" should be a prop of todo,not prop of data
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
].map(o=>({...o,show:false}))
},
methods: {
toggle: function(todo) {
todo.show = !todo.show
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="todo.show">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>