Dynamic class and color binding in VueJs? - javascript

I have this Custom Sort method that on my divs that arranges them ascending or descending. My question is how can i by default have the icon color greyed out and only once you click on the icon, it turns black while the others remain greyed out something like what vuetify data tables offers https://v15.vuetifyjs.com/en/components/data-tables.
Here is a link to my pen.
new Vue({
el: '#app',
data() {
return {
headers: [{
text: "Name",
value: "name"
}, // changed this to name
{
text: "Grades",
value: "grades"
}
],
labels: ["Andy", "Max", "John", "Travis", "Rick"],
Grades: [99, 72, 66, 84, 91],
sortKey: "", // added a sortKey,
direction: 1
}
},
computed: {
tableItems() {
let retVal = this.labels.map((label, i) => {
return {
name: label,
grades: this.Grades[i]
};
});
// if there is a sortKey use that
if (this.sortKey) {
retVal.sort((a, b) =>
this.direction * // here multiply by the direction
(a[this.sortKey] < b[this.sortKey] ? -1 : 1)
);
}
return retVal;
}
},
methods: {
sortBy(prop) {
if (this.sortKey === prop) {
this.direction *= -1 // change direction to -ve or positive
}
this.sortKey = prop;
console.log(prop);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout>
<v-flex v-for="header in headers" :key="header.text" xs4 py-1>
<span>
{{ header.text }}
<v-icon small #click="sortBy(header.value)">arrow_upward</v-icon>
</span>
</v-layout>
<v-layout v-for="item in tableItems" :key="item.name">
<v-flex xs4 py-1>
<span>{{ item.name }}</span>
</v-flex>
<v-flex xs4 py-1>
<span>{{item.grades}}</span>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
I am trying to replicate what vuetify data table offers but i am not able to figure out how would bind the color to the icon. i Just want to set the color of the icon and then change it to black or grey when it is clicked based on the header value.

This is low-tech but you could control the color with a (dynamic, bound) classname that keys off a method to test your sortKey (either on the icon itself, or on a parent element).
Forked Pen
:class="{ current: sortKey == header.value }"

You could create a method...
sortIconClass(val) {
return (this.direction===1 && val===this.sortKey)?'black--text':'grey--text'
}
And use it like...
<v-icon small #click="sortBy(header.value)" :class="sortIconClass(header.value)">arrow_upward</v-icon>
Codeply demo
For the icon, you can do something similar...
<v-icon small #click="sortBy(header.value)" :class="sortIconClass(header.value)">{{sortIcon(header.value)}}</v-icon>
sortIcon(val) {
return (this.direction===1 && val===this.sortKey)?'arrow_upward':'arrow_downward'
}
Updated Codeply

The icon itself is a component that has it's own state, you can do the same, create a single file component Icon.vue, then import it into the vue instance you are using right there
<template>
<i #click="setActive" v-bind:class="{ active: isActive }"></i>
</template>
<script>
data() {
return {
active: false
}
},
methods: {
setActive() {
active = true
}
},
computed: {
isActive() {
return active
}
}
</script>
<style scoped>
.active {
/* do whatever */
}
</style>
import:
import Icon from 'wherever/icon/is.vue'
/* ... */
components: {
'icon': Icon,
}

Related

How to put selected list values inside array in vue

I have a vue application where I have to select two elements from an list component and then put them inside an array.Right now I have my list and I can select them thanks to vuetify I binded it to an array with v-model I can console log it inside of an array but what I want to achieve is something like this:
{
"meetingName":"",
"meetingUrl":"",
"participants":{
participant1: "Hasan",
participant2: "Turan"
}
}
instead I am getting right now this:
{
"meetingName":"",
"meetingUrl":"",
"participants":[
"Hasan",
"Turan"
]
}
Could someone look at my code and tell me what is wrong with it?
html:
<template>
<v-container>
<v-row>
<v-col cols="4">
<v-card>
<v-list>
<v-list-item-group
v-model="model"
multiple
color="indigo"
v-model="model.participants"
>
<v-list-item
v-for="(item, i) in voterArrayFilteredByTime"
:key="item.voterUniqueName"
:value="item.voterUniqueName"
v-on:click= "generateGroup"
>
<v-list-item-content>
<v-list-item-title v-text="item.voterUniqueName"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</v-col>
</v-container>
</template>
an here is the method with which I want to console log it but it does not work like I said I am just getting numbers.
<script>
import axios from "axios";
export default {
name: "AddGroupsModal",
data : ()=>({
singleSelect: false,
selection: "",
model:{ meetingName: "", meetingUrl: "", participants: [] },
methods: {
generateGroup(){
console.log(this.model)
}
}
</script>
One of the problems in your markup is it has two bindings to v-model when there should be just one (the last one in this case):
<v-list-item-group
v-model="model"❌
multiple
color="indigo"
v-model="model.participants"❌
>
The v-list components can't create the expected value format, but you can use a computed prop along with Array.prototype.reduce to create an object from the array entries:
export default {
computed: {
computedParticipants() {
return this.model.participants.reduce((obj, name, i) => {
obj[`participant${i + 1}`] = name
return obj
}, {})
// OR:
const obj = {}
this.model.participants.forEach((name, i) => {
obj[`participant${i + 1}`] = name
})
return obj
},
},
}
demo

update google chart with selected period parameter from v-select

I am looking for a way to update the google bar type chart display
by selecting the period value from v-select.
so far, Initially, my script loaded, call 'async created()' and
get Data from REST API to MongoDB call module and store returned data in posts[].
and then finally save it to chart data array.
The data format is like this
Key
Type
Date (YYYY-MM-DD)
String
success_count
int
failure_count
int
what I wanna do is, each v-select values consist with {name, id} pair.
Is there any way to pass selected 'selectedItem.id' to
script's created(parameter) method or PostService.get_dashboard_Posts(parameter)'s parameter position?
without refreshing the whole page?
below is my CSS code
<template>
<v-app
:style="{ background: $vuetify.theme.themes.light.background }"
style="max-height:100vw;"
>
<v-container style="min-height:100%;">
<v-layout row class="ma-4">
<v-app style="background-color:grey lighten-1:" class="rounded">
<v-row>
<v-col cols="12" md="12" class="ligne">
<v-container>
<v-row>
<v-col cols="12" md="12">
<h1 class="heading mb-2 grey--text">Dashboard </h1>
<v-card>
<template>
<p class="error" v-if="error">{{error}}</p>
<div class = "posts-container">
<div class="post"
v-for="(post,index) in posts"
v-bind:item="post"
v-bind:index="index"
v-bind:key="post._id"
>
</div>
</div>
<div id="app">
<v-col cols="12" md="12">
<v-row>
<v-col cols="12" md="9"><h3 class="subTitle">Number Of Transactions</h3></v-col>
<v-col cols="12" md="3">
<v-select
:items="comboItem"
v-model="selectedItem"
item-text="name"
item-value="id"
label="Period"
single-line
dense
v-on:change=""
return-object>
</v-select>
</v-col>
</v-row>
<GChart
type="ColumnChart"
:data="chartData"
:options="chartOptions"
/>
</v-col>
</div>
</template>
</v-card>
</v-col>
</v-row>
</v-container>
</v-col>
</v-row>
</v-app>
</v-layout>
</v-container>
below is my script code
<script>
// # is an alias to /src
import PostService from "../PostService";
export default {
name: "PostComponent",
data: () => ({
posts:[],
comboItem:[
{
name: 'today',
id: 1
},
{
name: 'last week',
id: 2
},
{
name: 'last month',
id: 3
},
{
name: 'last year',
id: 4
},
],
chartData: [
["date", "success", "failure"]
],
chartOptions: {
chart: {
title: "Company performance",
subtitle: "Sales,Expences,and Profit:2016-2019"
},
hAxis: {
title: "date",
textStyle: {
fontSize:9
}
},
vAxis: {
title: "frequency",
textStyle: {
fontSize:15
}
},
legend: {
position : 'top',
alignment:'center'
}
}
}),
async created() {
try {
this.posts = await PostService.get_dashboard_Posts();
for(var i=0;i<Object.keys(this.posts[0]).length;i++){
this.chartData.push([`${this.posts[0][i]._id.year}-${this.posts[0]
[i]._id.month}-${this.posts[0][i]._id.day}`, this.posts[0][i]._id.success_count, this.posts[0]
[i]._id.failure_count])
}
} catch (err) {
this.error = err.message;
}
},
components: {},
computed: {
theme() {
return this.$vuetify.theme.dark ? "dark" : "light";
}
},
methods:{
}
};
</script>
Yes that is possible,
Basically we need to be able to respond to changes to selectedItem. All the loading currently happens inside the created function, which we cannot run again, so step 1 would be to move or copy the body of the created function to a new method, which we may call load or something. Now we can respond to changed to selectedItem by calling load.
async created() {
await this.load();
},
components: {},
computed: {
theme() {
return this.$vuetify.theme.dark ? "dark" : "light";
}
},
methods: {
async load() {
try {
this.posts = await PostService.get_dashboard_Posts();
for (var i = 0; i < Object.keys(this.posts[0]).length; i++) {
this.chartData.push([`${this.posts[0][i]._id.year}-${this.posts[0]
[i]._id.month}-${this.posts[0][i]._id.day}`, this.posts[0][i]._id.success_count, this.posts[0]
[i]._id.failure_count
])
}
} catch (err) {
this.error = err.message;
}
}
}
There are a few ways we can respond to changes, i'll show you a few:
We can define this behavior on the input element directly in the template:
<v-select
v-model="selectedItem"
...
v-on:change="load(selectedItem.id)"
...
>
</v-select>
Another way to tell vue to respond to changes is by using watchers:
...
async created() {
await this.load();
},
watch: {
selectedItem(newValue) {
this.load(newValue.id);
// At the moment the watcher runs this.selectedItem is already
// the newValue (this.selectedItem === newValue) so we could also
// do this:
this.load(this.selectedItem.id);
}
},
components: {},
...
The last thing that I haven't covered and leave up to you is to supply the selectedItem.id to PostService.get_dashboard_Posts

Pasting text including delimiters into Vuetify combobox does not separate the chips accordingly the delimiters?

I am trying to make a Vuetify combobox with chips splitting what I'm pasting into it according to the delimiters that are defined for that component, for instance ,. Meaning that if I'm pasting the text a,b,c, the component should turn them into 3 different chips: a, b and c but it does not work as such.
Full Vue Source code: https://codesandbox.io/s/chips-so-0gp7g?file=/src/domains/experimental/Experimental.vue
Preview: https://0gp7g.csb.app/experimental
Relevant piece of Vue Source Code:
<template>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="chips"
chips
:delimiters="[',']"
append-icon=""
clearable
hint="Hey I'm a 🥔 hint!"
persistent-hint
label="Type your favorite 🥔s"
multiple
solo
#update:search-input="meowInput"
>
<template v-slot:selection="{ attrs, item }">
<v-chip
v-bind="attrs"
close
:color="getColor(item)"
#click:close="remove(item)"
>
<strong>🥔{{ item }}</strong
>
</v-chip>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</template>
<script>
import ColorHash from "color-hash";
export default {
name: "Experimental",
components: {},
data() {
return {
select: [],
chips: [],
search: "", //sync search
};
},
methods: {
meowInput(e) {
console.log(e);
},
getColor(item) {
const colorHash = new ColorHash({ lightness: 0.9 });
return colorHash.hex(item);
},
remove(item) {
this.chips.splice(this.chips.indexOf(item), 1);
this.chips = [...this.chips];
},
},
};
</script>
Any idea about how can I achieve that behaviour?
You could bind and sync with the change of search-input, the rest is to split the search value and concat to the chips
search-input: Search value. Can be used with .sync modifier.
<v-combobox
// ...
:search-input.sync="search"
// ...
>
// ...
meowInput(e1) {
if (this.search && this.search.split(",").length > 1) {
this.chips = this.chips.concat(
this.search.split(",").filter((term) => !this.chips.includes(term))
);
this.search = "";
}
}
Forked demo

How to filter with a regex what's the user is typing in a Vuetify combobox to create a chip?

I am trying to allow only a certain regex pattern when the user is typing in a comboox to create or add a new chip (basically for example if you want the user to only be able to add phone number chips).
Full Vue Source code: https://codesandbox.io/s/chips-so-0gp7g?file=/src/domains/experimental/Experimental.vue
Preview: https://0gp7g.csb.app/experimental
Relevant piece of Vue Source Code:
<template>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="chips"
chips
:delimiters="[',']"
append-icon=""
clearable
hint="Hey I'm a 🥔 hint!"
persistent-hint
label="Type your favorite 🥔s"
multiple
solo
#input="meowInput"
#change="meowInput"
>
<template v-slot:selection="{ attrs, item }">
<v-chip
v-bind="attrs"
close
:color="getColor(item)"
#click:close="remove(item)"
>
<strong>🥔{{ item }}</strong
>
</v-chip>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</template>
<script>
import ColorHash from "color-hash";
export default {
name: "Experimental",
components: {},
data() {
return {
select: [],
chips: [],
search: "", //sync search
};
},
methods: {
meowInput(e) {
console.log(e);
},
getColor(item) {
const colorHash = new ColorHash({ lightness: 0.9 });
return colorHash.hex(item);
},
remove(item) {
this.chips.splice(this.chips.indexOf(item), 1);
this.chips = [...this.chips];
},
},
};
</script>
How can I achieve that behaviour?
The only way I can see this working is to evaluate the input against a regex (I used US numbers here, but you can use whatever you need) and, if it does not pass the regex test, pop the value out of the chips array.
You can see what I did below. Hopefully this gives you something to go off of:
<template>
<v-container>
<v-row>
<v-col>
<v-combobox
v-model="chips"
chips
:delimiters="[',']"
append-icon=""
clearable
hint="Hey I'm a 🥔 hint!"
persistent-hint
label="Type your favorite 🥔s"
multiple
solo
#change="meowInput" // I changed this to #change so it executes when the user hits enter
>
<template v-slot:selection="{ attrs, item }">
<v-chip
v-bind="attrs"
close
mask="###"
:color="getColor(item)"
#click:close="remove(item)"
>
<strong>🥔{{ item }}</strong
>
</v-chip>
</template>
</v-combobox>
</v-col>
</v-row>
</v-container>
</template>
<script>
import ColorHash from "color-hash";
export default {
name: "Experimental",
components: {},
data() {
return {
select: [],
chips: [],
search: "", //sync search
};
},
methods: {
meowInput(e) {
// Test if the input string matches the specified regex pattern
if (!/^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/.test(e)) {
console.log("invalid!");
// Remove from the chips array
console.log(this.chips);
this.chips.pop();
} else {
console.log("phone number");
// Automatically added to the chips array
}
},
getColor(item) {
const colorHash = new ColorHash({ lightness: 0.9 });
return colorHash.hex(item);
},
remove(item) {
this.chips.splice(this.chips.indexOf(item), 1);
this.chips = [...this.chips];
},
},
};
</script>

Check if string is in an array of objects VueJS?

I have a text field where a user can input the value and then on click of a button push the value inside an array. The value i want to test for is the uniqueID. So let's say if the user inputs multiple values including comma or white spaces and one of them matches the uniqueId in the existing array. Then i get an alert.
I tried using some but that didn't quite work. Not sure what i am doing wrong.
Here is a sample pen
new Vue({
el: "#app",
data() {
return {
myArray: [{
title: "Sam",
age: 36,
uniqueId: "GHTR435"
},
{
title: "Max",
age: 44,
uniqueId: "MLKT432"
}
],
inputField: "",
inputArray: []
};
},
methods: {
createArray() {
if (this.myArray.some((el) => el.uniqueID === this.inputField)) {
alert("Heyy!!!!");
} else {
this.inputArray.push(this.inputField.trim().replace(/, +/g, ","));
console.log(this.inputArray);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout justify-center>
<v-flex xs6>
<v-text-field solo v-model="inputField"></v-text-field>
<v-btn #click="createArray">Click Me</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
if (this.myArray.some((el) => this.inputField.split(/[, ]/g).includes(el.uniqueID))
You have an error in your condition. It should be
this.myArray.some((el) => el.uniqueId /* <-- "d" instead of "D" */ === this.inputField)

Categories

Resources