How can I disable literal values in Vuetify? - vuejs2

I am having problems when using the "item-disabled" prop on the v-select component from vuetify. I am trying to use this with literal options.
Here is the snippet which reproduces the issue:
In this example I would like to disable the option "Buzz".
Vue.use(Vuetify)
new Vue({
el: '#app',
data: () => ({
items: ['Foo', 'Bar', 'Fizz', 'Buzz'],
disabledItems: ['Buzz'],
})
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.14/vuetify.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.14/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-container fluid grid-list-xl>
<v-layout wrap align-center>
<v-flex xs12 sm6 d-flex>
<v-select :items="items" :item-disabled="disabledItems" box label="Box style"></v-select>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
<v-select :items="items" :item-disabled="disabledItems"></v-select>
...
items: ['Foo', 'Bar', 'Fizz', 'Buzz'],
disabledItems: ['Buzz'],
I do realize that I could use the non-literal key-value pair like in this example: https://codepen.io/anon/pen/joyoaj and it would work. But I would prefer to not have to write a wrapper component to convert literal options to key-value just to work around this.
<v-select :items="items"></v-select>
...
items: [
{text: 'Foo', value: 'Foo'},
{text: 'Bar', value: 'Bar'},
{text: 'Fizz', value: 'Fizz'},
{text: 'Buzz', value: 'Buzz', disabled: true},
],
Does anyone know how to get disabling literal values working?

You cant do it like that because item-disabled property is actually for something else.
From docs:
item-disabled
Default: disabled
Type: string | array | function
Set property of items's disabled value
So item-disabled just specifies which field on the objects will be treated as "disabled-field". By default, that field is disabled.
Without item-disabled you would have objects like this:
items: [
{text: 'Foo', value: 'Foo'},
{text: 'Buzz', value: 'Buzz', disabled: true},
],
And if objects have some other "disabled-property" (e.g. customDisabled) then use item-disabled prop like this:
<v-select :items="items" item-disabled="customDisabled"
// ...
items: [
{text: 'Foo', value: 'Foo'},
{text: 'Buzz', value: 'Buzz', customDisabled: true},
],
Codepen
If you need to preserve your arrays of strings then you could just map items to the array of objects and pass it:
<v-select :items="computedItems"
// ...
data: () => ({
items: ['Foo', 'Bar', 'Fizz', 'Buzz'],
disabledItems: ['Buzz'],
}),
computed: {
computedItems() {
return this.items.map(item => {
return {
text: item,
disabled: this.disabledItems.includes(item)
}
})
}
}
Codepen
Additionally, you can pass array to reach desired depth if your disabled field is nested, for example:
:item-disabled="['meta', 'disabled']"
// ...
{
text: item,
meta: {
disabled: true
}
}

Real working minimal example with function checking per item.
<v-select
:items="items"
item-text="name"
item-value="id"
:item-disabled="checkIsItemDisabled"
/>
<script>
data: function(){
return {
items: [
{id: 1, name: 'Foo'},
{id: 2, name: 'Bar'},
],
},
methods: {
checkIsItemDisabled(item) {
return (item.id === 1)
},
}
},
</script>

Adding the function option to #Traxo's answer:
<v-select :items="items" item-disabled="disableItem">
...
methods: {
disableItem(item) {
if (item.prop === this.anyOtherPropValue) {
return true;
}
return false;
},
},
Yes is reactive in case this.anyOtherPropValue change

Related

Vuetify v-data-table custom filter for dropdown

I have a v-data-table that already has the :search and :sort-by enabled. I have now created a select where I pull in my status from VueX. Accepted, Rejected. What I want to do is not only search or sort but when selecting from the drop down, accepted only the accepted values display in the table.
<v-select
v-model="selectStatus"
label="Status"
:items="statusData"
item-value="id"
item-text="name"
return-object
#change="filterStatus"
/>
Is this the correct way to setup the filter?
methods: {
filterStatus () {
console.log('This is where I am planning to add my custom filter')
}
}
This is my statusData:
userStatus : [
{
id: 0,
name: "Accepted",
},
{
id: 1,
name: " Rejected",
},
];
Or better to pass in the data:
{ text: 'Status', value: 'status.name', filter: value => {}},
To disable certain values to be selected add a disabled property to your items.
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
items: [{
id: 0,
name: 'Accepted'
},
{
id: 1,
name: ' Rejected',
disabled: true
}
],
value: null
}
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.0"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-container>
Value : {{value}}
<v-select
v-model="value"
label="Status"
:items="items"
item-value="id"
item-text="name"
return-object
/>
</v-container>
</v-app>
</div>
Documentation : props-disabled

How can bind change of object data instantly in Vue.js?

<div
v-if="!item.editNickname"
#click="item.editNickname=true, item.tmpNickname=item.nickname"
>{{item.nickname}}</div>
<v-text-field
v-model="item.tmpNickname"
v-if="item.editNickname||!item.nickname"
outlined
dense
hide-details
single-line
v-on:keyup.enter="saveNickname(item)"
/>
Here is my code.
I want to edit nickname in data table.
If user click nickname(div), text field will show up.
But this code cannot bind instantly(Actually value is binded correct, but UI is not react).
Can I make UI react without other action?
=====================================================
+INFO
This code block is in 'v-data-table' which basic component of vuetify.
The item is an Object.
You can do as below, I'm thinking tmpNickname is not one of the property that you are binding to the data table. See below for the sample code.
You can find the working code here
Template Code:
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="desserts"
class="elevation-1"
>
<template v-slot:item.nickname="{ item }">
<div v-if="!item.editable" #click="item.editable = true; item['tmpnickname'] = item.nickname">{{ item.nickname }}</div>
<v-text-field
v-model="item['tmpnickname']"
v-if="item.editable"
outlined
dense
hide-details
single-line
v-on:keyup.enter="saveNickname(item)"
/>
</template>
</v-data-table>
</v-app>
</div>
Script Code:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
headers: [
{
text: 'Col 1',
value: 'name',
},
{ text: 'Nick name', value: 'nickname' }
],
desserts: [
{
name: 'Row 1',
nickname: 'Name 1',
editable: false
},
{
name: 'Row 2',
nickname: 'Name 2',
editable: false
},
{
name: 'Row 3',
nickname: 'Name 3',
editable: false
}
],
}
},
methods: {
saveNickname (item) {
item.editable = false;
item.nickname = item.tmpnickname;
}
},
})

How can I sort a v-data-table by default?

I can't seem to get default sorting to work. All I can see for the argument custom-sort in the documentation is that it's a "Function used to sort items", but I don't know in what way. I can imagine many. Is it called for an initial sort? It seems to return a list of items, but when I try to create one, I get an error saying this.customSort is not a function.
<template>
<v-data-table
:headers="headers"
:items="reports"
hide-default-footer>
<template v-slot:item.requested="{ item }">
{{ datetimeToDistance(item.requested) }}
</template>
</v-data-table>
</template>
<script>
export default {
name: 'Reports',
data () {
return {
customSort: (items,index,isDesc) => console.log("never called"),
reports: [{name:"a",requested:"2020-01-01T00:00:00Z"}.{name:"b",requested:"2020-02-02T00:00:00"}],
}
},
computed: {
headers () {
return [
{text: "Name", value: "name"},
{text: "Report Type", value: "report_type"},
{text: "Requested", value: "requested", sort: (a,b) => a.localeCompare(b) },
];
},
}
}
</script>
My sorting works if you click on the links. All I really want here is to say: "When the page first loads, sort by 'requested' as though the user clicked that one initially. Then let them change the ordering."
Note: The datetimeToDistance is just a function which calls a library and isn't too important. It's just that the output of that column is not directly in the objects.
Use the sort-by and the sort-desc properties with the .sync option, and set the desired values in data.
<template>
<div>
<v-data-table
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
></v-data-table>
</div>
</template>
<script>
export default {
data () {
return {
sortBy: 'fat',
sortDesc: false,
},
}
</script>
https://vuetifyjs.com/en/components/data-tables/#external-sorting
I usually sort v-datatable in the pagination directive as below:
<template>
<v-data-table :headers="headers" :items="reports" :pagination.sync="pagination" hide-default-footer>
<template v-slot:item.requested="{ item }">
{{ datetimeToDistance(item.requested) }}
</template>
</v-data-table>
</template>
<script>
export default {
name: 'Reports',
data() {
return {
customSort: (items, index, isDesc) => console.log("never called"),
reports: [{ name: "a", requested: "2020-01-01T00:00:00Z" }.{ name: "b", requested: "2020-02-02T00:00:00" }],
pagination: { sortBy: 'requested', descending: true, rowsPerPage: 10 }
}
},
computed: {
headers() {
return [
{ text: "Name", value: "name" },
{ text: "Report Type", value: "report_type" },
{ text: "Requested", value: "requested", sort: (a, b) => a.localeCompare(b) },
];
},
}
}
</script>

Dynamically disable option in Vuetify overflow component

Vuetify disabled prop directly disabled input itself.I am trying to disable individual select option as per content on page.If we pass disabled="string" to items array (static arrangement).
How to make it dynamic.I have made Codepen for the same https://codepen.io/spider007/pen/eYmLBOG
<div id="app">
<v-app id="inspire">
<v-container>
<v-overflow-btn
class="my-2"
:items="items"
label="Overflow Btn - Dense"
dense
v-model="recordToAdd"
></v-overflow-btn>
<p v-if =" recordToAdd === '1' ">A's content is here -- i.e, Option A must be disabled in selection option</p>
</v-container>
</v-app>
</div>
JS
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
recordToAdd : "",
items: [
{text:"A",value:"1"},
{text:"B",value:"2"},
{text:"C",value:"3"},
],
}),
})
You just need to add disabled: true in the data.
<template>
...
<v-overflow-btn
class="my-2"
:items="dropdown"
label="Test"
segmented
target="#dropdown-example"
/>
...
</template>
...
data() {
return {
disabled: false,
dropdown: [
// Always disabled
{ text: 'disabled option', callback: () => console.log('disabled'), disabled: true },
// disable depending on another variable
{ text: 'depending', callback: () => console.log('Hello'), disabled: this.disabled },
{ text: 'other Option', callback: () => console.log('Option') },
]
}
},

How do I use "custom filter" prop in data tables in vuetify? or How do I create a custom filter to filter by headers?

As of date of posting, I cannot find any documentation to use the "custom filter" prop in data tables.
I just want to create a custom filter to filter my data table by headers.
I have a dropdown, and when user click on one of the options for the dropdown, it will filter the list for one specific header.
Example:
Dropdown options:
Food type: fruit, meat, vegetable
Bakchoi (vegetable)
Pork (meat)
Chicken Thigh (meat)
watermelon (fruit)
If I select dropdown as meat, it should only show me pork and chicken thigh.
Looking at the code on Github1, it looks like the customFilter prop is used to overwrite the default method used to determine how the filter prop is applied to the items in the table.
The default customFilter method applies the filter function to each property name of each item object and filters out any items that don't include one property name that passes the filter:
customFilter: {
type: Function,
default: (items, search, filter) => {
search = search.toString().toLowerCase()
return items.filter(i => (
Object.keys(i).some(j => filter(i[j], search))
))
}
},
You might want to overwrite this function if you wanted to prevent any columns from being included in the filter or if there were specific rows that you always wanted to prevent from being filtered out.
You'll notice that the method also depends on the search prop, which must be a string.
All that said, you really don't need to use that prop for what you want to do. You should just make a computed property to filter the items based on your dropdown value and pass that computed property as the items prop.
Here's an example:
new Vue({
el: '#app',
data() {
return {
food: [
{ name: 'Bakchoi', type: 'vegetable', calories: 100 },
{ name: 'Pork', type: 'meat', calories: 200 },
{ name: 'Chicken Thigh', type: 'meat', calories: 300 },
{ name: 'Watermelon', type: 'fruit', calories: 10 },
],
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Food Type', align: 'left', value: 'type' },
{ text: 'Calories', align: 'left', value: 'calories' },
],
foodType: null,
};
},
computed: {
filteredItems() {
return this.food.filter((i) => {
return !this.foodType || (i.type === this.foodType);
})
}
}
})
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#0.15.2/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#0.15.2/dist/vuetify.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons">
<div id="app">
<v-app>
<v-select
label="Food Type"
:items="['vegetable', 'meat', 'fruit']"
v-model="foodType"
></v-select>
<v-data-table
:headers="headers"
:items="filteredItems"
hide-actions
>
<template slot="items" scope="{ item }">
<td>{{ item.name }}</td>
<td>{{ item.type }}</td>
<td>{{ item.calories }}</td>
</template>
</v-data-table>
</v-app>
</div>
This answer was written when Vuetify was at v0.15.2. The source code for the VDataTable component at that version can be found here.
You can also go the customFilter approach like this, I've restricted the search to the type field.
new Vue({
el: '#app',
data() {
return {
food: [
{ name: 'Bakchoi', type: 'vegetable', calories: 100 },
{ name: 'Pork', type: 'meat', calories: 200 },
{ name: 'Chicken Thigh', type: 'meat', calories: 300 },
{ name: 'Watermelon', type: 'fruit', calories: 10 },
],
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Food Type', align: 'left', value: 'type' },
{ text: 'Calories', align: 'left', value: 'calories' },
],
search: '',
};
},
methods: {
customFilter(items, search, filter) {
search = search.toString().toLowerCase()
return items.filter(row => filter(row["type"], search));
}
}
})
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#0.15.2/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#0.15.2/dist/vuetify.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons">
<div id="app">
<v-app>
<v-select
label="Food Type"
:items="['vegetable', 'meat', 'fruit']"
v-model="search"
></v-select>
<v-data-table
:headers="headers"
:items="food"
:search="search"
:custom-filter="customFilter"
hide-actions
>
<template slot="items" scope="{ item }">
<td>{{ item.name }}</td>
<td>{{ item.type }}</td>
<td>{{ item.calories }}</td>
</template>
</v-data-table>
</v-app>
</div>
In my case I have 2 different way of filtering which are search bar and drop down. I tried to use custom-filter for both of them, but it doesn't work, so I came up with another approach
<v-text-field v-model="search" label="Label"></v-text-field>
<v-select
v-model="select"
:items="food"
item-text="type"
item-value="type"
:label="Types"
#change="filterFoodUsingDropDown"
>
</v-select>
<v-data-table
:search="search"
:headers="headers"
:items="food"
:custom-filter="filterFoodUsingSearchbar"
>
data() {
return {
food: [
{ name: 'Bakchoi', type: 'vegetable', calories: 100 },
{ name: 'Pork', type: 'meat', calories: 200 },
{ name: 'Chicken Thigh', type: 'meat', calories: 300 },
{ name: 'Watermelon', type: 'fruit', calories: 10 },
],
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Food Type', align: 'left', value: 'type' },
{ text: 'Calories', align: 'left', value: 'calories' },
],
search: '',
select: '',
};
},
methods: {
filterFoodUsingSearchbar(items, search, filter) {
// Condition
}
filterFoodUsingDropDown() {
if (this.select !== '') {
// In this case I use vuex to store the original data of the
food so that all the data is still exist even we filtered it out
this.food = this.$store.state.food.filter((item) => item.type === this.select)
}
}