Vuetify Autocomplete dropdown not working - vue.js

I am using vuetify autocomplete component in my nuxt project. its not working as expected. problem is when i start typing it fetches data using api but it doesn't show me results in dropdown, but once i click outside input box and then again focus on search box, then it shows the dropdown. but i want to see the results on every key press.
here is the code so far.
<v-autocomplete v-model="pickupairport" name="pickupairport" :items="airports" :loading="isLoading" :search-input.sync="aptsearch" chips clearable hide-details hide-selected label="Search for a airport..." solo>
<template v-slot:no-data>
<v-list-item>
<v-list-item-title>
Search for your favorite
<strong>Destinations</strong>
</v-list-item-title>
</v-list-item>
</template>
<template v-slot:selection="{ attr, on, item, selected }">
<v-chip v-bind="attr" :input-value="selected" color="blue-grey" class="white--text" v-on="on">
<v-icon left>
mdi-bitcoin
</v-icon>
<span v-text="item.name"></span>
</v-chip>
</template>
<template v-slot:item="{ item }">
<v-list-item-avatar color="indigo" class="text-h5 font-weight-light white--text">
{{ item.name.charAt(0) }}
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.name"></v-list-item-title>
<v-list-item-subtitle>{{item.cityName}}, {{item.countryName}}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-icon>fa-airports</v-icon>
</v-list-item-action>
</template>
</v-autocomplete>
this is script part
export default {
data() {
return {
aptsearch: null,
items: [],
model: null,
tab: null,
pickupairport: '',
airports: [],
isLoading: false,
uuid: this.$uuid.v4(),
}
},
watch: {
model(val) {
if (val != null) this.tab = 0
else this.tab = null
},
aptsearch(val) {
// Items have already been loaded
if (this.items.length > 0) return
this.isLoading = true
// Lazily load input items
fetch(`${process.env.EXPRESS_API_URL}/airports/find`, {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
aptsearchkey: this.aptsearch
})
})
.then(response => {
return response.json();
})
.then((data) => {
this.airports = []
data.forEach((item) => this.airports.push(item))
})
.catch(err => {
console.log(err)
})
.finally(() => (this.isLoading = false))
},
}

Related

Keyboard movement Vuetify v-list-item

I have a Vuetify v-list-item that is iterated over to create me a list, I want to be able to use the up and down arrows to traverse it. How can I do this? It doesn't seem to be default behavior.
The default focusing between elements behavior works through tab. You can try something like this in order to use arrows keys:
<template>
<v-card
class="mx-auto"
max-width="300"
tile
>
<v-list dense>
<v-subheader>REPORTS</v-subheader>
<v-list-item-group
v-model="selectedItem"
color="primary"
>
<v-list-item
v-for="(item, i) in items"
:key="i"
>
<v-list-item-icon>
<v-icon v-text="item.icon"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title v-text="item.text"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</template>
<script>
export default {
data: () => ({
selectedItem: 1,
items: [
{ text: 'Real-Time', icon: 'mdi-clock' },
{ text: 'Audience', icon: 'mdi-account' },
{ text: 'Conversions', icon: 'mdi-flag' },
],
}),
methods: {
nextItem () {
if (event.keyCode == 38 && this.selectedItem > 0) {
this.selectedItem--
} else if (event.keyCode== 40 && this.selectedItem < 3) {
this.selectedItem++
}
}
},
mounted () {
document.addEventListener("keyup", this.nextItem);
},
}
</script>

How to insert text search field for each table header for multi search function dynamically with v-for?

In order to achieve for multi search function we add a search field at each header with v-slot where we define the header name directly. For example:
<template v-slot:header.NAME="{ header }">
{{ header.text }}
<v-menu offset-y :close-on-content-click="false">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-bind="attrs" v-on="on"
v-model="nameSearch"
class="pa"
type="text"
></v-text-field>
</template>
</v-menu>
</template>
Now I have around 50 headers for my data table. My idea is to render dynamically. I tried with v-for but neither it renders and or give any error. Code below:
<template v-for="(x, index) in this.headers" slot="headers" slot-scope="props">
{{ props.x.text }}
<v-menu offset-y :close-on-content-click="false" :key="index">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-bind="attrs" v-on="on"
v-model="searchObj"
class="pa"
type="text"
></v-text-field>
</template>
</v-menu>
</template>
what did i missed here ? or it is completely wrong way i went ?
Below is the full code:
<template>
<v-app class="main-frame">
<v-main>
<v-data-table
:headers="headers"
:items="filteredData"
>
<template v-slot:header.NAME="{ header }">
{{ header.text }}
<v-menu offset-y :close-on-content-click="false">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-bind="attrs" v-on="on"
v-model="nameSearch"
class="pa"
type="text"
></v-text-field>
</template>
</v-menu>
</template>
<template v-slot:header.DEPARTMENT="{ header }">
{{ header.text }}
<v-menu offset-y :close-on-content-click="false">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-bind="attrs" v-on="on"
v-model="departmentSearch"
class="pa"
type="text"
></v-text-field>
</template>
</v-menu>
</template>
</v-data-table>
</v-main>
</v-app>
</template>
<script>
import axios from "axios";
}
export default {
name: 'Home',
data() {
return {
/* Table data */
headers: []
allData: [],
/* Column Search */
nameSearch: '',
departmentSearch: ''
}
},
computed: {
filteredData() {
let conditions = [];
if (this.nameSearch) {
conditions.push(this.filterName);
}
if (this.departmentSearch) {
conditions.push(this.filterDepartment);
}
if (conditions.length > 0) {
return this.allData.filter((data) => {
return conditions.every((condition) => {
return condition(data);
})
})
}
return this.allData;
}
},
mounted () {
this.getAllData()
},
methods: {
getAllData() {
this.allData = []
axios
.get("http://localhost:5001/"}
.then(res => {
if (res.status === 200) {
if (res.data === 0) {
console.log("Something is wrong !!!")
} else {
this.allData = res.data["allData"]
this.headers = res.data["allDataHeaders"]
}
}
}).catch(error => {
console.log(error);
})
},
filterName(item) {
return item.NAME.toLowerCase().includes(this.nameSearch.toLowerCase())
},
filterDepartment(item) {
return item.DEPARTMENT.toLowerCase().includes(this.departmentSearch.toLowerCase())
},
}
}
</script>
I have found the solution myself:
<template v-for="(header, i) in headers" v-slot:
[`header.${header.value}`]="{ }">
<div #click.stop :key="i">
{{ header.text }}
<v-text-field :key="i"
v-model="multiSearch[header.value]"
class="pa"
type="text"
:placeholder="header.value"
prepend-inner-icon="mdi-magnify"
></v-text-field>
</div>
</template>
In data, I have an empty object called:
searchObj: {}
and in computed method, i have the following method:
filteredData() {
if (this.searchObj) {
return this.allData.filter(item => {
return Object.entries(this.searchObj).every(([key, value]) => {
return (item[key] || '').toLowerCase().includes(value.toLowerCase())
})
})
} else {
return this.allData
}
},
Full solution below:
<template>
<v-app class="main-frame">
<v-main>
<v-data-table
:headers="headers"
:items="filteredData"
>
<template v-for="(header, i) in headers" v-slot:
[`header.${header.value}`]="{ }">
{{ header.text }}
<div #click.stop :key="i">
<v-text-field :key="i"
v-model="multiSearch[header.value]"
class="pa"
type="text"
:placeholder="header.value"
prepend-inner-icon="mdi-magnify"
></v-text-field>
</div>
</template>
</v-data-table>
</v-main>
</v-app>
</template>
<script>
import axios from "axios";
}
export default {
name: 'Home',
data() {
return {
/* Table data */
headers: []
allData: [],
searchObj: {},
}
},
computed: {
filteredData() {
if (this.searchObj) {
return this.allData.filter(item => {
return Object.entries(this.searchObj).every(([key, value]) => {
return (item[key] ||'').toLowerCase().includes(value.toLowerCase())
})
})
} else {
return this.allData
}
},
},
mounted () {
this.getAllData()
},
methods: {
getAllData() {
this.allData = []
axios
.get("http://localhost:5001/"}
.then(res => {
if (res.status === 200) {
if (res.data === 0) {
console.log("Something is wrong !!!")
} else {
this.allData = res.data["allData"]
this.headers = res.data["allDataHeaders"]
}
}
}).catch(error => {
console.log(error);
})
},
}
}
</script>

I want to add editing functions using Nuxt.js

What I want to come true
I am creating TodoLists.
I tried to implement the following editing features, but it didn't work and I'm having trouble.
Click the edit button to display the edit text in the input field
If you click the save button after entering the changes in the input field, the changes will be reflected in the first position.
Code
<v-row v-for="(todo,index) in todos" :key="index">
<v-text-field
filled
readonly
:value="todo.text"
class="ma-3"
auto-grow
/>
<v-menu
top
rounded
>
<template #activator="{ on, attrs }">
<v-btn
v-bind="attrs"
icon
class="mt-6"
v-on="on"
>
<v-icon>
mdi-dots-vertical
</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item
link
>
<v-list-item-title #click="toEdit(todos)">
<v-icon>mdi-pencil</v-icon>
Edit
</v-list-item-title>
</v-list-item>
</v-list>
<v-list>
<v-list-item
link
>
<v-list-item-title #click="removeTodo(todo)">
<v-icon>mdi-delete</v-icon>
Delete
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-row>
<v-text-field
v-model="itemText"
filled
color="pink lighten-3"
auto-grow
#keyup.enter="addTodo"
/>
<v-btn
:disabled="disabled"
#click="addTodo"
>
Save
</v-btn>
data () {
return {
editIndex: false,
hidden: false,
itemText: '',
items: [
{ title: 'Edit', icon: 'mdi-pencil' },
{ title: 'Delete', icon: 'mdi-delete' }
]
}
},
computed: {
todos () {
return this.$store.state.todos.list
},
disabled () {
return this.itemText.length === 0
}
},
methods: {
addTodo (todo) {
if (this.editIndex === false) {
this.$store.commit('todos/add', this.itemText)
this.itemText = ''
} else {
this.$store.commit('todos/edit', this.itemText, todo)
this.itemText = ''
}
},
toEdit (todo) {
this.editIndex = true
this.itemText = this.todos
},
removeTodo (todo) {
this.$store.commit('todos/remove', todo)
}
}
}
</script>
export const state = () => ({
list: []
})
export const mutations = {
add (state, text) {
state.list.push({
text
})
},
remove (state, todo) {
state.list.splice(state.list.indexOf(todo), 1)
},
edit (state, text, todo) {
state.list.splice(state.list.indexOf(todo), 1, text)
}
}
Error
Click the edit button and it will look like this
What I tried myself
//methods
toEdit (todo) {
this.editIndex = true
this.itemText = this.todos.text //add
},
// Cannot read property 'length' of undefined
For some reason I get an error that I couldn't see before
The properties/data types in your code are a bit mixed up.
Here you're accessing state.todos.list...
todos () {
return this.$store.state.todos.list
},
...but in your store the const state doesn't include todos:
export const state = () => ({
list: []
})
Furthermore, you're writing to itemText the content of todos, which should be a string but actually is an object - which leads to the output of [object Object].
toEdit (todo) {
this.editIndex = true
this.itemText = this.todos
},
Also, please check out kissu's comment about the mutations.

Cannot execute method when v-btn clicked with Vuetify

I can't execute method when clicking v-btn.
my template
<template>
<v-app>
<v-main>
<v-card class="mx-auto" max-width="500" tile>
<v-text-field
label="Main input"
hide-details="auto"
v-model="newTodo"
></v-text-field>
<v-list disabled>
<v-subheader>TODOS</v-subheader>
<v-list-item-group v-model="todos" color="primary">
<v-list-item v-for="todo in todos" v-bind:key="todo.id">
<v-list-item-content>
<v-list-item-title v-text="todo.text"></v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn color="blue-grey" class="white--text" v-on:click="test">
<v-icon dark>mdi-delete</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</v-main>
</v-app>
</template>
my script
<script>
export default {
name: "app",
data: () => ({
todos: [],
newTodo: ""
}),
created() {
this.reload();
},
methods: {
reload() {
let vm = this;
const axios = require("axios").create({
baseURL: "http://localhost:5000",
headers: {
"Content-Type": "application/json"
},
responseType: "json"
});
axios
.get("/get_todos")
.then(function(response) {
// handle success
console.log(response);
vm.todos = response.data;
})
.catch(function(error) {
// handle error
console.log(error);
})
.finally(function() {
// always executed
});
},
test() {
console.log("test");
}
}
};
</script>
What I've tried
I replaced the "v-on:click" with "#click", "#onclick" and "#click.native" but failed to see "test" on the console.
Version of plugins
Vue: v2.6.12
Vuetify: v2.3.16
As Michal LevĂ˝ said, I tried removing disabled from v-list. I succeeded to execute test method.

vuetify dropdown get data's by using vue Infinite loading

I tried to get data for my vuetify drop down list by using vue-infinite-loading. function is not calling automatically.
Below is my code
<v-menu offset-y offset-x bottom left max-height="500" min-width="400" max-width="450">
<template v-slot:activator="{ on }">
<el-badge :value="unReadMessage" :hidden="hidden" class="item">
<v-icon color="white" v-on="on">notifications</v-icon>
</el-badge>
</template>
<v-list v-for="(item, index) in notification_data" :key="index">
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>{{item.title}}</v-list-tile-title>
<v-list-tile-sub-title>{{item.description}}</v-list-tile-sub-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
<infinite-loading :identifier="infiniteId" #infinite="infiniteHandler" ref="infiniteLoading"></infinite-loading>
</menu>
Below is written in script. I already imported this "import InfiniteLoading from 'vue-infinite-loading';"
When i put the infinite loader out of the menu, its working but it shown the value out of the list
<script>
methods: {
infiniteHandler($state) {
this.loading = true;
this.axios.get(api, {
params: {
page: this.page,
},
})
.then(({data}) => {
if (data.next !== null) {
this.page += 1;
this.notification_data = this.notification_data.concat(data.results);
this.loading = false;
$state.loaded();
}
else {
this.loading = false;
this.notification_data = this.notification_data.concat(data.results);
$state.complete();
}
})
.catch(error => {
this.$notify({
type: 'error',
title: 'Error!',
message: error,
});
});
},
},
components: {
InfiniteLoading
}
</script>