Vuetify datatable search prop with autocomplete component - vue.js

I'm trying to configure a drop down filter for each column in my vuetify data table. It seems the vuetify autocomplete component has the functionality I want but I'm not sure how to get the values from the autocomplete component to filter the data table. Here's what I have:
<template>
<v-card>
<v-card-title>
{{gridTitle}}
<v-spacer></v-spacer>
</v-card-title>
<v-data-table
v-model="selected"
:headers="headers"
:items="dataSource"
class="elevation-1"
:search="search"
:loading="isloading"
item-key="id"
show-select
multi-sort
dense
>
<template v-slot:header.id="{ header }" >
<v-autocomplete
v-model="search"
:items="dataSource"
item-text="id"
:label="header.value"
v-bind="selected"
dense
multiple
chips
small-chips
filled
>
</v-autocomplete>
</template>
<v-progress-linear
slot="progress"
color="blue"
indeterminate
></v-progress-linear>
<v-alert
slot="no-results"
:value="true"
color="error"
icon="warning"
>Your search for "{{ search }}" found no results.</v-alert>
</v-data-table>
</v-card>
</template>
<script>
export default {
name: "ConfirmationsGrid",
data() {
return {
isloading: true,
search: "",
selected: [],
};
},
props: {
dataSource: {
type: Array[Object],
default: new Array(),
},
headers: Array[String],
gridTitle: String,
},
mounted() {
this.isloading = false;
},
methods: {
onSelectMethod: function (value) {
this.$emit("select_method", value);
},
},
};
</script>
At the moment I'm testing with the one header. slot but I plan on extending to all headers. This renders the autocomplete as the column header and also shows the correct values in the drop down but selecting them doesn't filter the table. I only get the following error:
[Vue warn]: Invalid prop: type check failed for prop "search". Expected String with value "2579034", got Array
Any ideas on how I can convert the autocomplete data into a string?

You are adding the prop multiple to the v-autocomplete tag with v-model search, so it returns an Array, i.e.:
["search option1", "searchOption2"]
But the v-autocomplete tag with v-model selected is using the search prop as String, so that's te reason of the error.
The default behavior of the search prop for v-autocomplete tag is to match the string passed to it with each value in its options.
To test it, remove the multiple prop in the search v-autocomplete tag, and you can see that it works.
Now, in order to work with multiple word search, you can have a computed property as items for the first v-autocomplete:
...
computed: {
itemsForSelected() {
if (this.search.length) {
return this.dataSource.filter(item => this.search.includes(item))
}
return this.dataSource
}
}
...
<template>
<v-card>
<v-card-title>
{{gridTitle}}
<v-spacer></v-spacer>
</v-card-title>
<v-data-table
v-model="selected"
:headers="headers"
:items="itemsForSelected"
class="elevation-1"
:loading="isloading"
item-key="id"
show-select
multi-sort
dense
>
<template v-slot:header.id="{ header }" >
<v-autocomplete
v-model="search"
:items="dataSource"
item-text="id"
:label="header.value"
v-bind="selected"
dense
multiple
chips
small-chips
filled
>
</v-autocomplete>
</template>
<v-progress-linear
slot="progress"
color="blue"
indeterminate
></v-progress-linear>
<v-alert
slot="no-results"
:value="true"
color="error"
icon="warning"
>Your search for "{{ search }}" found no results.</v-alert>
</v-data-table>
</v-card>
</template>
Now you can remove the search prop from the first v-autocomplete, this solution is not useful if you want to have the search by substrings ("hol" matching "alcohol"), it just filter the items from the source that are not selected in the search filter. A better solution could be includes a regex to match more options.

Related

Vuetify update select input-value by prepend-item event

I'd like to update the select value by clicking the prepended item in a Vuetify select. But the model doesn't update the item-text in the select input.
<v-select
:items="loadedTasks"
v-model="selectedTask"
item-text="text"
item-value="value"
return-object
:menu-props="{closeOnContentClick: true}"
>
<template v-slot:prepend-item>
<v-list-item
#click="selectedTask = {text:'none', value: 'none'}"
>
Text
</v-list-item>
</template>
If you are trying to add some "default" item to the select, the prepend-slot is no way to go as it cannot be "selected". It's better to just add your "default" item directly into the loadedTasks array
For example using computed:
computed: {
tasksToSelect() {
return [{text:'none', value: 'none'}, ...this.loadedTasks]
}
}
...and use it like <v-select :items="tasksToSelect" />

vuetify v-file-input not select file the first time

I am using v-file-input to select file for uploading. The issue I am facing is that the file is not selected the first time. The dialog from where file is to be selected reopens then I have to select the file again and then it gets selected. Below is my code. Please help me find where I am going wrong. I am using vuetify 2.3.10
<v-file-input
placeholder="Upload Document"
required
:rules="uploadDocument"
#change="(file) => onSelection(file)"
>
<template v-slot:selection="{ text }">
<v-chip
small
label
color="primary"
>
{{ text }}
</v-chip>
</template>
</v-file-input>
A v-model on the v-file-input is enough
<template>
<v-container class="px-0" fluid>
<v-file-input placeholder="Upload Document" required v-model="file" #emptied="file = null">
<template v-slot:selection="{ text }">
<v-chip small label color="primary">{{ text }}</v-chip>
</template>
</v-file-input>
Selected filename: {{file? file.name : ''}}
</v-container>
</template>
<script>
export default {
props: ["car"],
data() {
return {
file: null,
}
},
methods: {
},
};
</script>
Good luck.

How to avoid mutating a prop directly when all you have is a click?

How can I mutate a prop the correct way, so that I don't get the [Vue warn]: Avoid mutating a prop directly message?
I already got the v-model to work on this v-dialog. However, I also want to provide a close button in the dialog itself, which causes this mutation warning, as it's the dialog itself that's mutating the variable. How best to approach this case and solve it?
Dialog.vue:
<template>
<v-dialog
:value="value" #input="$emit('input', $event)"
scrollable
width="80vw"
:transition="false"
>
<template v-slot:activator="{ on }">
<div v-on="on" #click="$emit('open')">
<slot name="button">
<v-btn color="primary">{{ buttonText == null ? title : buttonText }}</v-btn>
</slot>
</div>
</template>
<v-card
elevation="10"
height="80vh"
>
<v-system-bar
color="light-blue darken-3"
window
>
<span>{{title}}</span>
<v-spacer></v-spacer>
<v-icon #click="value=false">mdi-close</v-icon>
</v-system-bar>
<v-card-text> <!-- required here to make the scrollable v-dialog work -->
<slot></slot>
</v-card-text>
<slot name="actions"></slot>
</v-card>
</v-dialog>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
buttonText: String,
value: Boolean,
},
}
</script>
I can use it quite nicely like:
<mydialog title="Select something" button-text="A button!" v-model="dialog" #open="loadData()">
Content goes here...
</mydialog>
The <v-icon #click="value=false">mdi-close</v-icon> is what's incorrectly mutating the "value"-variable.
Sidenote #1: The open event is there so I can populate data (loadData) from a database when the dialog is opened (vs. when its created on the DOM).
UPDATE; I can get it to work by doing:
<v-icon #click="$emit('close', $event)">mdi-close</v-icon>
and
<mydialog title="Select something" button-text="A button!" v-model="dialog" #open="loadData()" #close="dialog=false">
However, I feel this is far from being elegant. Aren't there any solutions in where I don't need to add on-Handlers to close this dialog? I almost feel that this is worse than living with the warning.. :|
Use a computed property with get and set. The dialog computed will behave exactly as a normal variable and it will eliminate the warning. Now you can use it to get the value and also to set the value.
Try this:
<template>
<v-dialog
:value="dialog"
#input="$emit('input', $event)"
scrollable
width="80vw"
:transition="false"
>
<template v-slot:activator="{ on }">
<div v-on="on" #click="dialogOpened()">
<slot name="button">
<v-btn color="primary">{{ buttonText == null ? title : buttonText }}</v-btn>
</slot>
</div>
</template>
<v-card elevation="10" height="80vh">
<v-system-bar color="light-blue darken-3" window>
<span>{{title}}</span>
<v-spacer></v-spacer>
<v-icon #click="dialog=false">mdi-close</v-icon>
</v-system-bar>
<v-card-text>
<!-- required here to make the scrollable v-dialog work -->
<slot></slot>
</v-card-text>
<slot name="actions"></slot>
</v-card>
</v-dialog>
</template>
export default {
props: ['title', 'buttonText', 'value'],
data: () => ({
dlg_close: false,
}),
computed: {
dialog: {
get() {
return this.value;
},
set(selection) {
this.$emit("input", selection);
}
}
},
methods: {
dialogOpened(newVal) {
this.dialog = newVal;
},
},
}

Vuetify timepicker return value to model

I'm using Vuetify timepicker within my project, however after selecting a time it doesn't appear to be updating my model, how can I get it to do this?
This is my component which is using the timepicker:
time-select.vue
<template>
<v-layout row wrap>
<v-flex>
<v-menu
ref="menu"
v-model="menu2"
:close-on-content-click="false"
:nudge-right="40"
:return-value.sync="time"
lazy
transition="scale-transition"
offset-y
full-width
max-width="290px"
min-width="290px">
<v-text-field
slot="activator"
v-model="time"
:label="this.label"
:name="this.name"
prepend-icon="access_time"
readonly></v-text-field>
<v-time-picker
v-if="menu2"
v-model="time"
full-width
#click:minute="$refs.menu.save(time)"
></v-time-picker>
</v-menu>
</v-flex>
</v-layout>
</template>
<script>
export default {
props: [ 'label', 'name', 'value' ],
data () {
return {
time: this.value,
menu2: false,
modal2: false
}
}
}
</script>
And this is how I'm adding the timepicker to my page, as you can see I'm using v-model so the value is updated.
<time-select v-model="hours.open_time"
name="businessHoursTimeFrom[]"
label="Open Time"></time-select>
Don't forget that v-model="someVar" used with custom components is just a shortcut for:
:value="someVar" #input="someVar = $event"
Thus, this:
<time-select v-model="hours.open_time" />
Under the hood is:
<time-select :value="hours.open_time" #input="hours.open_time = $event" />
That means that your custom component should trigger input event itself.
Like this:
<v-time-picker
v-if="menu2"
:value="time"
#input="$emit('input', $event)"
full-width
#click:minute="$refs.menu.save(time)" />
Useful link: Vue.js → Using v-model on components

Extracting the information in a prop in a Vue child component

I'm passing a object as a prop to a child component but I can't reference one of its elements (user_id) to use in a method in that child component.
My code (slightly abbreviated) is:
<template>
<div class="back">
<v-app id="inspire">
<v-content>
<v-container fluid>
<v-card flat>
<v-card-title>
<div>
<div class="headline">
{{data.title}}
</div>
<span class="grey--text">{{data.user}} said {{data.created_at}}</span>
</div>
<v-spacer></v-spacer>
<v-badge color="deep-orange accent-3" left overlap>
<span slot="badge">7</span>
<v-icon color="grey lighten-1" large>
insert_comment
</v-icon>
</v-badge>
<!--<v-btn color="deep-orange accent-3">5 replies</v-btn>-->
</v-card-title>
<v-card-text v-html="data.body"></v-card-text>
<v-card-actions v-if="own">
<v-btn icon small>
<v-icon color="deep-orange accent-3">edit</v-icon>
</v-btn>
<v-btn icon small>
<v-icon color="red">delete</v-icon>
</v-btn>
</v-card-actions>
</v-card>
<return-button></return-button>
</v-container>
</v-content>
</v-app>
</div>
</template>
<script>
export default {
name: "ShowQuestion",
props: ['data'],
data() {
return {
own: this.Own(),
}
},
methods: {
Own: function () {
return this.UserID() == this.user_id <---HERE BE DRAGONS! (reported as 'undefined')
},
UserID: function () {
... returns the 'sub' from the JWT token
return sub;
}
},
}
</script>
While the correct information is being displayed in the template, I also need to be able to compare the user's ID from the token with that contained in the prop (i.e. data.user_id). My reading suggests the solution will involve converting the object to an array, but that's beyond my current skill level too. I'd appreciate some pointers to a solution.
Thanks,
Tom
If you can render data.user_id in your template you can use it anywhere, but I'd probably do something like this to solve your problem:
props: ['data']
data() {
return {
}
},
computed: {
UserId() {
return //however you get your id
}
},
Then your v-if could just be this:
<v-card-actions v-if="UserId === data.user_id">