I have an array cars which returns me the names and each of them has a property starred which i want to toggle true and false back and forth. However i want to set starred to true for only one of them at a time. So i created a method setStarred and inside the method, i am using a map to set others to false. However i am able to set the starred to true however i am not able to set it back to false.
Please check this Codepen
This is working example
new Vue({
el: "#app",
data() {
return {
cars: [{
name: "Toyota",
starred: false
},
{
name: "BMW",
starred: false
},
{
name: "Ford",
starred: false
}
]
};
},
methods: {
setStarred(index) {
this.cars.map((i) => {
i.starred = false;
});
this.cars[index].starred = !this.cars[index].starred;
}
}
});
<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 column>
<v-flex xs6 v-for="(car,index) in cars" :key="index">
<h2>{{car.name}}
<v-icon :color="car.starred ? 'primary': '' " #click="setStarred(index)">star_border
</v-icon>
</h2>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Essentially i am trying to set the selected back to false. Any help will be appreciated. Thank you
Try this:
this.cars[index].starred = !this.cars[index].starred;
this.cars.map((i) => {
if(i.name!=this.cars[index].name)
i.starred = false;
});
I prefer saving the 'starred' state of the target first, then toggle it later.
If so, you don't need to put one if statement in the for-loop. Though in this case, it doesn't improve the performance a lot, but I believe avoid too many if from for-loop is one good habit.
Below is the example:
new Vue({
el: "#app",
data() {
return {
cars: [{
name: "Toyota",
starred: false
},
{
name: "BMW",
starred: false
},
{
name: "Ford",
starred: false
}
]
};
},
methods: {
setStarred(index) {
let cur = this.cars[index].starred
this.cars.forEach((i) => {
i.starred = false;
});
this.cars[index].starred = !cur;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.18/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 column>
<v-flex xs6 v-for="(car,index) in cars" :key="index">
<h2>{{car.name}}
<v-icon :color="car.starred ? 'primary': '' " #click="setStarred(index)">star_border
</v-icon>
</h2>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Related
There is scenario where there is right drawer in which grade dropdown with list grade1, grade2, grade3 etc is been displayed and on all pages I need v-alert notification strip to display total count of student. So, for example from right drawer if I select grade3 then on v-alert strip total student of grade 3 will be displayed. If I change the dropdown value in right drawer to grade1 then total number of student for grade 1 will be reflected on v-alert on all pages.
NOTE - v-alert strip should be displayed on all pages just like navbar.
How can I achieve this?
Should I use stores for this?
I am not sure about the component structure you have but I am demoing based on the assumption that you have both drawer and v-alert in a master page (parent page) and other routes are loading inside it. If my understanding is correct. You can give a try to this :
Vue.component('child', {
data() {
return {
items: [
{ title: 'Grade 1', icon: 'mdi-view-dashboard', count: 5 },
{ title: 'Grade 2', icon: 'mdi-view-dashboard', count: 10 },
{ title: 'Grade 3', icon: 'mdi-view-dashboard', count: 15 },
],
}
},
template: `<v-navigation-drawer permanent>
<v-divider></v-divider>
<v-list dense nav>
<v-list-item v-for="item in items" :key="item.title" link #click="getStudentCount(item.title)">
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>`,
methods: {
getStudentCount(title) {
this.$emit('student-count', this.items.find(obj => obj.title === title).count)
}
}
});
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
right: null,
studentCount: 0
}
},
methods: {
getStudentCount(e) {
this.studentCount = e
}
}
});
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.13/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.13/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons"/>
<div id="app">
<v-app id="inspire">
<v-card height="400" width="256" class="mx-auto">
<v-alert border="left" color="indigo" dark>
Count : {{ studentCount }}
</v-alert>
<child #student-count="getStudentCount"></child>
</v-card>
</v-app>
</div>
I have a text field in which you can input the field and on Click it adds a value to an array which renders the chips. Now each chips has a value property and a toggle property. By Default every new chip has toggle set to false. Now i can add as well as delete chips. The chips are deleted by using the deleteChips method. What i am trying to do is when there is only one chip left, the chip's toggle value should change dynamically to false or true depending on whatever it was at the time. So basically toggle = !toggle.
I have tried using the array.length but wasn't able to get it working.
Here is a sample pen
Here is the sample code:-
new Vue({
el: "#app",
data() {
return {
inputValue: "",
inputArray: [],
selectedChip: ""
};
},
methods: {
createChips() {
this.inputArray.unshift({
value: this.inputValue,
toggle: false
});
this.inputValue = "";
},
deleteChips(chip) {
let index = this.inputArray.filter((el) => el.chip === chip);
this.inputArray.splice(index, 1);
},
chipSelection(item) {
this.selectedChip = item;
},
toggleChip(item) {
item.toggle = true;
}
}
});
<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 v-model="inputValue"></v-text-field>
<v-btn #click="createChips">Click Me</v-btn>
<div v-if="inputArray.length > 0">
<div v-for="chip in inputArray">
<v-chip :key="chip.value" close #click="chipSelection(chip.value)" #input=deleteChips(chip)>
<v-avatar>
<v-icon #click="toggleChip(chip.value)">
account_circle
</v-icon>
</v-avatar>
<span v-if="selectedChip === chip.value">{{chip.value}}</span>
</v-chip>
</div>
</div>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
tbh, I can't really follow your intent, but based on the comments, perhaps you want to watch the inputArray and execute code based on changes to its length.
watch: {
inputArray(newValue, oldValue) {
if ((newValue.length !== oldValue.length) && (newValue.length === 1)) {
this.inputArray[0].toggle = !this.inputArray[0].toggle;
}
}
}
I am using vuetify and trying to create a method to add chips from the dropdown. Now i got most of the logic down but as you can see, the method multipleSelection does not fire off on the enter key but works fine onClick.
Here is a demo for the codepen.
So the method multipleSelection should fire off on the enter key.
new Vue({
el: '#app',
data() {
return {
arr: [],
items: [{
text: 'Foo',
value: 'foo'
},
{
text: 'Bar',
value: 'bar'
},
{
text: 'biz',
value: 'buzz'
},
{
text: 'buzz',
value: 'buzz'
}
],
}
},
methods: {
multipleSelection(item) {
this.arr.push(item)
console.log(this.arr)
},
deleteChip(item) {
console.log('delete')
this.arr = this.arr.filter(x => x !== item);
console.log(this.arr)
}
},
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<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>
<div id="app">
<v-app id="inspire">
<v-container>
<v-combobox :items="items" label="Add Multiple Chips" multiple small-chips solo deletable-chips :value="arr">
<template v-slot:item="{ index, item }">
<v-list-tile-content #click.stop.prevent="multipleSelection(item)">
{{item.text}}
</v-list-tile-content>
</template>
<template v-slot:selection="{ index, item }">
<v-chip close dark color="info" #click:close="deleteChip(item)">
{{ item.text }}
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
Since multipleSelection() is not being called from #keypress on v-slot:item, likely it's not where the event is being captured.
Taking a look at events on Vue Dev Tools, can see input $emit by <VCombobox> is the first one after pressing Enter.
So the following will get it, but this seems to mess with the position in the list for some reason I don't understand.
<v-combobox
...
#input.capture="(item) => multipleSelection(item)"
>
Better to add a listener,
mounted() {
this.$refs.combobox.$on('input', (items) => {
const item = items[items.length -1]; // getting all selected, so take the last
this.multipleSelection(item)
})
},
Note, I tested this on a local project with Vuetify v1.5.14.
Looks like you need #keyup
<v-list-tile-content
#keyup.enter.prevent="multipleSelection(item)" #click.stop.prevent="multipleSelection(item)">{{item.text}}
</v-list-tile-content>
Not sure about keypress....Vue docs show #keyup
https://v2.vuejs.org/v2/guide/events.html#Key-Modifiers
I'm currently implementing a data table using Vuetify, but I have stumbled into a problem while trying to react to a click on the "select-all" button in a data table. Right now, when the select-all button is clicked, the currently visible rows are selected (which is exactly what I want). However, I would like to be notified of the user clicking on this select-all button.
My plan is to provide the user with a "select everything" button, once the user clicks this "select-all" checkbox, but I can't seem to find a way (without having to reside to hacks) to get notified of a click on the "select-all" button.
There is a toggle-select-all method.
<div id="app">
<v-app id="inspire">
<v-data-table
:value="selected"
#toggle-select-all="selectAll"
:items-per-page="itemsPerPage"
:headers="headers"
:items="desserts"
item-key="name"
show-select
class="elevation-1"
>
</v-data-table>
<v-dialog>
<v-card>
</v-card>
</v-dialog>
</v-app>
</div>
Javascript is below:
new Vue({
el: '#app',
vuetify: new Vuetify(),
methods: {
selectAll(event) {
if (event.status) {
alert('selected all')
} else {
alert('deselected all')
}
}
},
data () {
return {
selected: [],
itemsPerPage: 10,
headers: ['headers', 'data'],
desserts: ['your', 'data']
}
}
})
You can do this by using :value and #input on the Vuetify table rather than v-model. Check if the selected array is equal to items per page when the user selects something.
<div id="app">
<v-app id="inspire">
<v-data-table
:value="selected"
#input="enterSelect($event)"
:items-per-page="itemsPerPage"
:headers="headers"
:items="desserts"
item-key="name"
show-select
class="elevation-1"
>
</v-data-table>
<v-dialog>
<v-card>
</v-card>
</v-dialog>
</v-app>
</div>
Javascript is below:
new Vue({
el: '#app',
vuetify: new Vuetify(),
methods: {
enterSelect(values) {
if (values.length == this.itemsPerPage) {
alert('selected all')
}
}
},
data () {
return {
selected: [],
itemsPerPage: 10,
headers: ['headers', 'data'],
desserts: ['your', 'data']
}
}
})
Working example here: https://codepen.io/CodingDeer/pen/QWLyaog?editors=1010
We use vuetify autocomplete component https://vuetifyjs.com/ru/components/autocompletes to display key/value pairs.
When I open page to create new entity all works fine but when when I open page to modify entity where fields must be filled in with saved values then autocomplete field does not display value.
Here is the example of entity model:
{name : "name", legalId : "123", mcc : {id : "1", description : "text"}}. Items variable has format
[{id : "1", description : "text"}, {id : "2", description : "text"}]
So how it is possible to display "description" property value from model.mcc.description?
<template>
<div>
<v-form class="mt-5">
<v-text-field
v-validate="'required|max:255'"
v-model="model.name"
:error-messages="errors.collect('name')"
:class="inputClass('name')"
label="Name"
data-vv-name="name"
required
></v-text-field>
<v-text-field
v-validate="'required|max:255'"
v-model="model.legalId"
:error-messages="errors.collect('legalId')"
:class="inputClass('legalId')"
label="Legal ID"
data-vv-name="legalId"
required
></v-text-field>
<v-autocomplete
v-model="model.mcc"
:items="items"
:loading="isLoading"
:search-input.sync="searchMccCodes"
:class="inputClass('mccCode')"
color="white"
hide-no-data
hide-selected
item-text="description"
item-value="id"
label=""
placeholder="MCC Code"
></v-autocomplete>
</v-form>
</div>
</template>
<script>
import axios from 'axios';
import queries from '../../utils/queries';
export default {
name: 'MPayMerchantEditor',
props: ['merchant', 'loading', 'showCancel'],
components: {},
data: () => ({
model: {},
isLoading: false,
items: [],
searchMccCodes: null,
required: true,
}),
computed: {
isFormValid() {
return !Object.keys(this.fields)
.some(key => this.fields[key].invalid);
},
isNew() {
return !this.merchant;
},
},
methods: {
submit() {
this.$validator.validateAll()
.then((isValid) => {
if (isValid) {
this.$validator.reset();
this.$emit('submit', this.model);
} else {
this.showValidationErrorMessage();
}
});
},
cancel() {
this.$validator.reset();
this.$emit('cancel', this.model);
},
inputClass(name) {
if (this.fields[name]) {
const changed = this.fields[name].changed;
return { 'merchants-editor__input__not-changed': !changed };
}
return {};
},
storeMerchant() {
if (this.merchant) {
Object.keys(this.merchant)
.forEach((key) => {
this.model[key] = this.merchant[key];
});
this.items.push(this.model.mcc);
this.$validator.reset();
}
},
},
mounted() {
this.storeMerchant();
},
created() {
merchant);
},
watch: {
merchant() {
this.storeMerchant();
},
searchMccCodes(value) {
if (!value) {
return;
}
this.isLoading = true;
axios.get(queries.merchantMccCodes(value))
.then((response) => {
this.items = response.data;
this.isLoading = false;
})
.catch((e) => {
this.showError(e);
this.isLoading = false;
});
},
},
};
</script>
You need to use the "selection" scoped slot.
<v-autocomplete
v-model="model.mcc"
:items="items"
:loading="isLoading"
:search-input.sync="searchMccCodes"
:class="inputClass('mccCode')"
color="white"
hide-no-data
hide-selected
item-text="description"
item-value="id"
label=""
placeholder="MCC Code"
return-object
>
<template slot="selection" slot-scope="data">
{{ data.item.description }}
</template>
</v-autocomplete>
And you should probably add return-object param to the autocomplete tag.
As you can see in this snippet, you must ensure the property mcc is created before the render the component. it should throw an error in console telling you mcc is undefined
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app>
<v-main>
<v-container>
<v-autocomplete
v-model="model.mcc"
:items="items"
color="white"
hide-no-data
hide-selected
item-text="description"
item-value="id"
placeholder="MCC Code"
/>
</v-container>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
model: {mcc: {description: ''}},
items: [{id:0, description: 'test 1'}, {id:1, description: 'test 2'}]
})
})
</script>
</body>
</html>
Have you tried to add return-object to your autocomplete tag ?
Following snippet worked for me:
<v-autocomplete
[...]
:filter="filter"
:return-object="true"
>
<template slot="selection" slot-scope="data">
{{ data.item.description}}
</template>
<template slot="item" slot-scope="data">
<div>{{ data.item.description}}</div>
</template>
</v-autocomplete>
and add following script:
methods: {
filter (item, queryText, itemText) {
if (queryText != null && item != null && item.description) {
return item.description.toLocaleLowerCase().indexOf(queryText.toLocaleLowerCase()) > -1
}
return -1
}
}