Are there any possible ways to search multiple attribute in v-autocomplete in Vuetify? - vue.js

I have an array of objects bind to item prop of v-autocomplete.
this is my data:
products: [
{
text: "Apple",
value: 209
},
{
text: "Banana",
value: 229
}
]
<v-autocomplete>
...
:item="products"
:search-input.sync="search"
</v-autocomplete>
so, i want to have the ability to search by both 'text' and 'value'. Currently, i am able to search only one among them.

Use custom filter function to v-autocomplete as follows
<v-autocomplete>
...
:filter="customFilter"
:item="products"
:search-input.sync="search"
</v-autocomplete>
methods: {
customFilter (item, queryText, itemText) {
// return true or false according to your logic
// i.e queryText matches with item object
},
},

Yes, you can search multiple attribute using filter attribute which is provide in the docs:
customSearch(item, queryText, itemText) {
const data = item.text.toLowerCase() + item.value.toLowerCase()
const searchText = queryText.toLowerCase()
return textOne.indexOf(searchText) > -1
}
and use in the template like this:
<v-autocomplete
spellcheck="false"
:filter="customSearch"
...
</v-autocomplete>
Refer this docs: https://vuetifyjs.com/en/api/v-autocomplete/#props

Related

Vue-i18n and v-select - can't translate

I have a problem to translate the information with vue i18n in my v-select. All others translation work but not this one... And i don't find a solution ...
HTML :
<v-card-text>
<v-select v-model="model" :items="propsList" :items-text="propsList.text" label="Select a reason:" clearable />
</v-card-text>
DATA eg : ($t = i18n)
propsList: [
{ text: this.$t('XXX.A') as string, value: 'X' },
{ text: this.$t('XXX.B') as string, value: 'Y' },
{ text: this.$t('XXX.C') as string, value: 'Z' },
],
Traduction :
{ "en": {
"XXX" : {
"A": "A", ...}},
"fr": {
"XXX" : {
"A": "A", ...}},
In my App.vue :
data ... :
languages: [
{ text: 'English', value: 'en' },
{ text: 'Français', value: 'fr' },
],
watch: {
language(val: string) {
this.setLanguage(val);
this.$i18n.locale = val;
},
},
I'm keep trying ! But thanks by advance :)
There is a prop called :get-option-label that is useful for i18n, that way you can just pass the i18n key in your option array:
<v-select :options="options" :get-option-label="option => $t(option.text)"></v-select>
Then options would look like this:
options = [
{ text: "XXX.A", value: 0 }
{ text: "XXX.B", value: 1 }
{ text: "XXX.C", value: 3 }
]
More details: https://vue-select.org/api/props.html#getoptionlabel
v-select doesn't have a prop named 'items-text' (items with s). You probably mean item-text (without s).
the prop item-text is used to specify the "path" where each item of your data has the text, it defaults to the string "text" which means the text of the item is found at the property "text"
if for example, you have the item with the structure:
{
value: 'some value',
name: 'John'
}
You should pass the string "name".
since you have your data with the text as text property, your template should look like this:
<v-card-text>
<v-select v-model="model" :items="propsList" label="Select a reason:" clearable />
</v-card-text>
Other options for this prop are:
An array of strings
Use this for nested properties, say for example you have your item's structure as follows:
{
value: 'whatever',
data: {
name: {
en: 'John'
}
}
}
you should pass ['data', 'name', 'en'], Vuetify will resolve the name.
A callback function
The callback function you pass will be called for each item and the item itself will be passed as a parameter, you should return whatever string you want to be displayed, this might be useful if you want to concatenate two properties of your items, e.g. first name and last name. or to display a prefix based on some value

Dynamic item template slots within v-data-table with custom components & helpers

Say I have a custom component that uses Vuetify's v-data-table within.
Within this component, there's multiple other custom components such as loaders and specific column-based components for displaying data in a certain way.
I found myself using the same code for filtering, retrieving data, loaders etc. across the project - so not very DRY.
The things that vary are:
API request url to retrieve data from (which I can pass to this generic component)
headers for v-data-table (which I pass to this generic component)
specific item slot templates!
(One file using this same code would need a column modification like the below, requiring different components sometimes too):
<template v-slot:[`item.FullName`]="{ item }">
<router-link class="black--text text-decoration-none" :to="'/users/' + item.Id">
<Avatar :string="item.FullName" />
</router-link>
</template>
Where another would have for example:
<template v-slot:[`item.serial`]="{ item }">
<copy-label :text="item.serial" />
</template>
There are many more unique "column templates" that I use obviously, this is just an example.
modifying items passed to v-data-table in a computed property (to add "actions" or run cleanups and/or modify content before displaying it - not related to actual HTML output, but value itself)
computed: {
items () {
if (!this.data || !this.data.Values) {
return []
}
return this.data.Values.map((item) => {
return {
device: this.$getItemName(item),
serial: item.SerialNumber,
hwVersion: this.$getItemHwVersion(item),
swVersion: this.$getItemSwVersion(item),
actions: [
{ to: '/devices/' + item.Id, text: this.$t('common.open') },
{ to: '/devices/' + item.Id + '/replace', text: this.$t('common.replace') }
],
...item
}
})
}
there are some unique methods that I can use on certain template slot item modifications, such as dateMoreThan24HoursAgo() below:
<template v-slot:[`item.LastLogin`]="{ item }">
<span v-if="dateMoreThan24HoursAgo(item.LastLogin)">{{ item.LastLogin | formatDate }}</span>
<span v-else>
{{ item.LastLogin | formatDateAgo }}
</span>
</template>
I can always make this global or provide them as a prop so this point should not be a big issue.
So my questions are:
What is the best way to use one component with v-data-table within but dynamically pass template slots and also allow item modification prior to passing the array to the v-data-table (as per point 3 and 4 above)
is there a better way to approach this since this seems too complex (should I just keep separate specific files)? It does not feel very DRY, that's why I'm not very fond of the current solution.
Basically I would be happy to have something like:
data: () => {
return {
apiPath: 'devices',
headers: [
{ text: 'Device', align: 'start', value: 'device', sortable: false, class: 'text-none' },
{ text: 'Serial Number', sortable: false, value: 'serial', class: 'text-none' },
{ text: 'Status', value: 'Status', class: 'text-none' },
{ text: 'Calibration', value: 'NextCalibrationDate', class: 'text-none' },
{ text: '', sortable: false, align: 'right', value: 'actions' }
],
itemsModify: (items) => {
return items.map((item) => {
return {
device: this.$getItemName(item),
serial: item.SerialNumber,
actions: [
{ to: '/devices/' + item.Id, text: this.$t('common.open') },
{ to: '/devices/' + item.Id + '/replace', text: this.$t('common.replace') }
],
...item
}
})
},
columnTemplatesPath: '/path/to/vue/file/with/templates'
}
}
And then I'd just call my dynamic component like so:
<GenericTable
:api-path="apiPath"
:headers="headers"
:items-modify="itemsModify"
:column-templates-path="columnTemplatesPath"
/>
Relevant but not exactly a solution to my question:
Is it possible to use dynamic scoped slots to override column values inside <v-data-table>?
Dynamically building a table using vuetifyJS data table

Vue - How to use Getter / Setting in v-model with v-for

Some reason my getter is not pulling in the data from my Vuex Store when I use a getter / setter in my v-for loop. I'm not getting any errors, the data just isn't being pulled in like it should.
Here is my code.
My v-for loop (truncated)
<template
v-for="(detail,index) in details"
>
<v-row
:key="detail.title"
>
<v-col
cols="12"
>
<v-textarea
solo
auto-grow
flat
:placeholder="detail.placeholder"
v-model="detail.model"
rows="2"
row-height="10px"
></v-textarea>
</v-col>
</v-row>
</template>
My data element details where the loop is coming from:
details: [
{
title: "DESCRIBE YOUR BRIDAL GOWN: WHO DESIGNED IT? WHAT STYLE?",
placeholder: "include your details here",
model: "gown",
showDivider: true
},
{
title: "WHERE’S YOUR WEDDING? INCLUDE LOCATION DETAILS (E.G., ADDRESS, TYPE, DESTINATION)",
placeholder: "include your details here",
model: "weddingLocation",
showDivider: true
},
{
title: "WHAT DO YOU WANT YOUR BRIDESMAIDS TO WEAR? THINK PINTEREST BOARDS, NECKLINES, COLORS, AND LENGTH.",
placeholder: "include your details here",
model: "theme",
showDivider: false
},
]
Then finally my getters / setters. By themselves, outside the for loop, they work fine. So its not the store or the getters/setter but somehow the interaction between the loop and those computed properties:
computed: {
...mapGetters('wedding', [
'returnWedding',
]),
date: {
get() {
let day = this.returnWedding.day > 9 ? '' + this.returnWedding.day : '0' + this.returnWedding.day
let month = this.returnWedding.month > 9 ? '' + this.returnWedding.month : '0' + this.returnWedding.month
let year = this.returnWedding.year
return year + "-" + month + "-" + day
},
set(value) {
this.updateLocalWedding({ bridalGown: value })
}
},
gown: {
get() {
return this.returnWedding.bridalGown
},
set(value) {
this.updateLocalWedding({ bridalGown: value })
}
},
weddingLocation: {
get() {
return this.returnWedding.locationDetails
},
set(value) {
this.updateLocalWedding({ locationDetails: value })
}
},
theme: {
get() {
return this.returnWedding.theme
},
set(value) {
this.updateLocalWedding({ theme: value })
}
},
}
What you're trying to do isn't directly possible.
You'd need something like this
<v-textarea v-model="thisVueInstance[detail.model]" />
in order to reference the computed properties by name. The problem is, you can't reference the current Vue instance in the template like that. Neither of these options work
<v-textarea v-model="this[detail.model]" />
<v-textarea v-model="[detail.model]" />
What I've found does work is to set up a computed property to return the current Vue instance, eg
computed: {
// ...
form: vm => vm
}
Then use this in your template as the object reference base
<v-textarea v-model="form[detail.model]" />

Allocate an entire object to Vuetify v-select v-model

I have vuetify select component as follows:
<v-select
class="ml-4"
v-model="selectedProjects"
:items="projects"
item-text="name"
item-value="id"
chips
:menu-props="{ maxHeight: '400' }"
label="Select Projects"
multiple
hint="Select projects to overlay"
persistent-hint
></v-select>
"projects" is an array of objects, and thus I can reference name and id in the item-name and item-value fields. At the moment selectedProjects is just an array of ids, but I want it to be an array of the actual selected objects contained in projects. How can I do that?
You should be able to get the selected objects by setting the return-object prop, which the Vuetify docs describes thusly:
Changes the selection behavior to return the object directly rather
than the value specified with item-value
Does this work?
<template>
<v-select :items="selectProjects" v-model="selectedProject" #change="filterMe" />
</template>
<script>
export default {
data() {
return {
selectedProject: 1,
projects: [
{ id: 1, name: "John Doe", artist: "Some artist" },
{ id: 2, name: "Doe John", artist: "Some artist" }
]
};
},
computed: {
selectProjects() {
return this.projects.map(project => {
return {
value: project.id,
text: project.name
};
});
}
},
methods: {
filterMe() {
let item = this.projects.filter(
project => this.project == project.id
)[0];
console.log(item);
}
}
};
</script>
When you select an option, it will use the Object ID as v-model but display text as the select value, you should be able to filter the ID after if needed.

Vue.js. Render of different kinds of child components according to a list of conditions

I'm new to not OOP, and VUE.JS especially.
I have a list of conditions, according to them I should show on the page several different kinds of components.
How can I render, for example, 2 TextInput components (or 3.. 10) dynamically and read the entered text in parent after clicking a button?
Thank you in advance.
You didn't provide any code, so I'm not sure what exactly you are trying to do.
If you want to display multiple components, just use v-for and specify conditions in v-if, which will detemine whether this particular component will be rendered:
<input
v-for="input in inputs"
v-if="input.show"
v-model="input.model"
:placeholder="input.label"
type="text"
>
<button #click="handleButtonClick()">Button text</button>
data: () => ({
inputs: [
{
label: 'input 1',
model: '',
show: true
},
{
label: 'input 2',
model: '',
show: true
}
]
}),
methods: {
handleButtonClick () {
console.log(this.inputs)
}
}
If you don't know the type of component you need to display you can use dynamic components.
In a nutshell this defers the type of component used at runtime based on a value.
Let's assume you have 2 different type of components
TextComponent
ImageComponent
You can have a list of items
data () {
return {
items: [
{
id: 1,
val: 'something',
type: 'TextComponent'
},
{
id: 2,
val: 'something',
type: 'ImageComponent'
}
]
}
}
Now you can iterate over the list and display the component based on type
<component v-for="item in items" :key="item.id" :is="item.type" v-model="item.value />
If type is not the exact name of the component, you can translate it inside the :is condition. Something like this.
:is="getComponentFromTag(item.type)"
And then write the conversion method
methods: {
getComponentFromTag (tag) {
switch (tag) {
case 'text':
return 'TextComponent'
case 'img':
return 'ImageComponent'
}
}
}
For the example above I'm assuming that items look like this:
items: [
{
id: 1,
val: 'something',
type: 'text'
},
{
id: 2,
val: 'something',
type: 'img'
}
]