VuetifyJS Advanced slots autocomplete with Google Places API - javascript

I need to get VuetifyJS advanced slots to work with the Google Places API. Currently some addresses only show up in the autocomplete dropdown after clicking the "x" in the form field to delete the input text.
Here is a CodePen demonstrating the issue:
https://codepen.io/vgrem/pen/Bvwzza
EDIT: I just found out that populating the dropdown menu with the suggestions is the issue. The suggestions are visible in the console.log but not in the dropdown. Any ideas how to fix this issue?
(Some addresses work right away, some not at all - it's pretty random.
Any ideas on how to fix this are very welcome.)
JS:
new Vue({
el: "#app",
data: () => ({
isLoading: false,
items: [],
model: null,
search: null,
}),
watch: {
search(val) {
if (!val) {
return;
}
this.isLoading = true;
const service = new google.maps.places.AutocompleteService();
service.getQueryPredictions({ input: val }, (predictions, status) => {
if (status != google.maps.places.PlacesServiceStatus.OK) {
return;
}
this.items = predictions.map(prediction => {
return {
id: prediction.id,
name: prediction.description,
};
});
this.isLoading = false;
});
}
}
});
HTML:
<div id="app">
<v-app id="inspire">
<v-toolbar color="orange accent-1" prominent tabs>
<v-toolbar-side-icon></v-toolbar-side-icon>
<v-toolbar-title class="title mr-4">Place</v-toolbar-title>
<v-autocomplete
v-model="model"
:items="items"
:loading="isLoading"
:search-input.sync="search"
chips
clearable
hide-details
hide-selected
item-text="name"
item-value="symbol"
label="Search for a place..."
solo
>
<template slot="no-data">
<v-list-tile>
<v-list-tile-title>
Search for a <strong>Place</strong>
</v-list-tile-title>
</v-list-tile>
</template>
<template slot="selection" slot-scope="{ item, selected }">
<v-chip :selected="selected" color="blue-grey" class="white--text">
<v-icon left>mdi-map-marker</v-icon>
<span v-text="item.name"></span>
</v-chip>
</template>
<template slot="item" slot-scope="{ item, tile }">
<v-list-tile-avatar
color="indigo"
class="headline font-weight-light white--text"
>
{{ item.name.charAt(0) }}
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title v-text="item.name"></v-list-tile-title>
<v-list-tile-sub-title v-text="item.symbol"></v-list-tile-sub-title>
</v-list-tile-content>
<v-list-tile-action> <v-icon>mdi-map-marker</v-icon> </v-list-tile-action>
</template>
</v-autocomplete>
<v-tabs
slot="extension"
:hide-slider="!model"
color="transparent"
slider-color="blue-grey"
>
<v-tab :disabled="!model">Places</v-tab>
</v-tabs>
</v-toolbar>
</v-app>
</div>
(I enabled the relevant API's in Google Cloud.)

the problem is the number of request you do in a certain amount of time. Every character triggers an request to the GoogleApi which is resulting in.
I think the error_message isn't totally correct while I trying afterwards it gives me a result.
To solve this,
upgrade your GoogleApi account
Debounce your input. so wait till the customer is not typing for half a second and then sen a request to The googleApi. You could use lodash to implement to debounce functionality https://lodash.com/docs/#debounce

Related

Vue.js & vuex handling SMS by server-side-events

I have a app, which is basically a Call centrum. You can receive calls, call to someone, receive sms and send sms etc.. I have problem with showing my SMS on screen, when I receive event from backend, I am showing that data on screen using vuex and V-for for specific component. Problem is that when I receive another event from backend with different number, I would like to show it under that first sms, but it will overwrite first sms and show only that new sms. I was trying multiple approaches, but nothing worked for me so I hope someone will be able to show me my mistake.
Here is photo of screen with one sms (red box is where second sms should be with own informations like number...)..
Here is code where I receive events.
export default function setupStream(){
let evtSource = new EventSource('/hzs/events.sse');
evtSource.addEventListener('receive_sms', event => {
let sms_data = JSON.parse(event.data);
store.dispatch('receiveSMS', sms_data);
}, false)
}
Here is my vuex code
const state = {
sms: [],
};
const getters = {
getSMS: (state) => state.sms,
};
const actions = {
receiveSMS({ commit }, sms_data) {
commit('setSMS', sms_data);
},
};
const mutations = {
setSMS: (state, sms) => (state.sms = sms),
};
export default {
state,
getters,
actions,
mutations
}
And here is component.
<template>
<v-card>
<v-card-title class="primary white--text">
{{ $t("Communication") }}
</v-card-title>
<v-card d-flex flex-column height="100%" class="card-outter scroll">
<v-col>
<div v-for="sms in getSMS" :key="sms.id">
<v-card-actions>
<v-row>
<v-btn #click="openChat" icon class="mt-4"
><v-img
max-width="30px"
max-height="30px"
class="mt-2"
src="#/assets/icons/icon-sms.svg"
alt="icon-sms"
/></v-btn>
<v-col>
<span>{{sms.date_time}}</span> <br />
<h4>{{sms.sender}}</h4>
<!-- Dialog for Adding new Note -->
<v-dialog
v-model="showEditor"
max-width="400px"
persistent
scrollable
>
<template v-slot:activator="{ on, attrs }">
<v-btn
#click="showEditor = true"
depressed
small
v-bind="attrs"
v-on="on"
>{{$t("Add Note")}}</v-btn
>
</template>
<AddNoteDialog v-on:close-card="showEditor = false"
/></v-dialog>
</v-col>
<v-spacer></v-spacer>
<v-btn class="mt-5" icon #click="deleteCommunication"
><v-img
max-width="20px"
src="#/assets/icons/icon-delete.svg"
alt="icon-delete"
/></v-btn>
</v-row>
</v-card-actions>
<v-divider></v-divider>
</div>
<v-spacer></v-spacer>
<v-divider></v-divider>
<v-card-actions class="card-actions">
<v-row>
<v-text-field
class="ml-4"
color="primary white--text"
required
:label="$t('Mobile number')"
clearable
></v-text-field>
<v-dialog
v-model="showEditor1"
max-width="450px"
persistent
scrollable
>
<template v-slot:activator="{ on, attrs }">
<v-btn
#click="showEditor1 = true"
class="mt-5 mr-4"
depressed
icon
v-bind="attrs"
v-on="on"
><v-icon>mdi-plus-circle</v-icon></v-btn
>
</template>
<AddNummberDialog v-on:close-card="showEditor1 = false"
/></v-dialog>
</v-row>
</v-card-actions>
</v-col>
</v-card>
</v-card>
</template>
<script>
import AddNoteDialog from "#/components/UI/AddNoteDialog";
import AddNummberDialog from "#/components/UI/AddNummberDialog";
import { mapGetters, mapActions } from 'vuex';
export default {
name: "Communication",
data() {
return {
dialog: false,
showEditor: false,
showEditor1: false,
note: '',
chat: this.switchChat,
};
},
computed: {
...mapGetters(['getSMS']),
},
components: { AddNoteDialog, AddNummberDialog },
props: ["switchChat"],
methods: {
...mapActions(['setupEvents']),
openChat() {
this.$emit('openChat')
},
async deleteCommunication() {
alert("Deleted");
},
},
};
</script>
<style>
.scroll {
overflow-y: scroll;
}
.card-outter {
padding-bottom: 50px;
}
.card-actions {
position: absolute;
bottom: 0;
width: 100%;
}
</style>
I think that solution is creating new array, where I will store every single SMS that I receive. Problem is that I don't know how and where to do it.
You already have your vue state array called sms which is a good start. You'll need to update your Vuex to have an additional mutation called "addNewSMS" or something:
const mutations = {
setSMS: (state, sms) => (state.sms = sms),
addNewSMS: (state, newSMS) => state.sms.push(newSMS),
};
This will update your state.sms array to include more than one element, which you should be able to loop through using a v-for loop in your template.
Of course, you'll also need to update your actions like this:
const actions = {
receiveSMS({ commit }, sms_data) {
commit('addNewSMS', sms_data);
},
};
As a sidenote, I'd personally change the sms variable name to messages so its clearer to you and other coders that it contains multiple objects.

how to display every value of an nested array

In my vue application I have a nested array where the user can select one date and multiple times and this is saved as one object. What I want to achieve now is to display the date as a header (Works) and under it display all times belonging to that specific date. Right now I could only make it work with one time. But if one object has multiple times I still could only make it work to show one time.
Could someone take a look at my code?
code to display the insides of the array:
<v-card-text >
<v-row>
<v-col cols="4" v-for="(i, index) in datesFinal" >
<h4>{{i.date}}</h4>
<v-chip>{{i.times[0].startTime + ":" + i.times[0].endTime}}</v-chip>
</v-col>
</v-row>
</v-card-text>
Script tag:
<script>
import MeetingsTableComponent from "#/components/MeetingsTableComponent";
import DatePickerComponent from "#/components/DatePickerComponent";
export default {
name: "NextMeetingCardComponent",
data: () => ({
selectedTime: [],
dates: new Date().toISOString().substr(0,10),
datesFinal: [],
dialog: false,
menu: false,
modal: false,
menu2: false,
menu3: false
}),
methods:{
addTimeFields(){
this.selectedTime.push({
startTime:"",
endTime: "",
})
},
saveDateAndTIme(e) {
this.datesFinal.push({
date: this.dates,
times: this.selectedTime
}
)
this.selectedTime = [];
}
My method does not work because I am telling it to access [0] but this is just my guess
You could try this solution::
<v-card-text>
<v-row>
<v-col cols="4" v-for="(item, index) in datesFinal" :key="index" >
<h4>{{item.date}}</h4>
<v-chip-group v-if="item.times.length !== 0">
<v-chip v-for="(time, i) in item.times" :key="i">
{{time.startTime + ":" + time.endTime}}
</v-chip>
</v-chip-group>
<v-chip-group v-else>
<v-chip>no times </v-chip>
</v-chip-group>
</v-col>
</v-row>
</v-card-text>
Ummm, I think I can provide a thinking that maybe you can use Set to filter the same date. Then use every item of this set to gather the same date's time from the object.

How to filter data using boolean(True or False) in VueJS?

I'm creating a button to filter those data(games) that a user bought or have though in my code it only filters data with no boolean attribute set upon them.
Edited: added some more details on the code with firebase data to ref
<!-- This is the button I'm having trouble with -->
<v-tooltip right>
<v-btn small flat color="grey" #click="toggleHave(true)" slot="activator">
<v-icon left small>title</v-icon>
<span class="caption text-lowercase">by All titles bought</span>
</v-btn>
<span>Sort by Game's Title that I have</span>
</v-tooltip>
</v-flex>
<!-- filterGames is for the search method -->
<v-layout row wrap>
<v-card v-for="game in filterGames" :key="game.id" class="ma-2" width="240px">
<router-link :to="{ name: 'view-game', params: { game_id: game.game_id }}">
<v-img :src="game.cover" :alt="game.name" />
</router-link>
<v-card-title>
<div>
<span class="subheading">{{ game.title }}</span><br>
<span class="caption grey--text">{{ game.platform }}</span><br>
</div>
</v-card-title>
</v-card>
</v-layout>
export default {
data() {
return {
games: [], //this is connected to firebase
search: '',
}
},
toggleHave(bought) {
console.log(bought)
this.games = this.games.filter(game => {
return game.have === bought.have
})
},
computed: {
filterGames() {
return this.games.filter((game) => {
return game.title.toLowerCase().match(this.search)
})
}
}
}
// with true
game_id: "006"
title: "Far Cry 4"
have: true // this is the boolean
// with false
game_id: "051"
title: "Assassin's Creed Unity"
have: false // this is the boolean
You're calling toggleHave(true), but in your toggleHave function you use argument as it was an object:
game.have === bought.have
That means you compare game.have with undefined, which explains current behaviour.
Replace bought.have with bought and it should work.
Your question is more of a JavaScript question then a vue question. The filter function returns an array of the elements that meet your condition see here
If you would like to receive Boolean on filtering try using the includes method like here
see this Filter list with Vue.js
just change
return this.games.filter(game => {
return game.have == bought
})

Using Vuetify how can I place a search bar over a carousel?

As the title says, I want to place a search bar functionality over a carousel and I'm trying to achieve this using <v-autocomplete> and v-carousel>.
Using the code snippets from the official Vuetify docs, I have managed to have this:
<template>
<v-layout
justify-center
app
>
<v-flex
xs12
sm6
>
<v-container
fluid
grid-list-md
>
<v-layout
row
wrap
>
<!--Carousel-->
<v-flex xs6>
<v-carousel
hide-controls
hide-delimiters
height='200'
interval='2500'
>
<v-toolbar
dark
color="teal"
>
<v-toolbar-title>State selection</v-toolbar-title>
<v-autocomplete
v-model="select"
:loading="loading"
:items="items"
:search-input.sync="search"
cache-items
dense
hide-no-data
hide-details
label="What state are you from?"
solo-inverted
>
</v-autocomplete>
<v-btn icon>
<v-icon>more_vert</v-icon>
</v-btn>
</v-toolbar>
<v-carousel-item
v-for="(item,i) in items"
:key="i"
:src="item.src"
>
</v-carousel-item>
</v-carousel>
</v-flex>
</v-layout>
</v-container>
</v-flex>
</v-layout>
</template>
<script>
<!--I use selective imports. This might not be needed for you when trying to reproduce it -->
import VContainer from "vuetify/lib/components/VGrid/VContainer";
import VFlex from "vuetify/lib/components/VGrid/VFlex";
import VLayout from "vuetify/lib/components/VGrid/VLayout";
import VCard from "vuetify/lib/components/VCard/VCard";
import VImg from "vuetify/lib/components/VImg/VImg";
import VCarousel from "vuetify/lib/components/VCarousel/VCarousel";
import VAutocomplete from "vuetify/lib/components/VAutocomplete/VAutocomplete";
export default {
name: "HomeContents",
data: () => ({
loading: false,
items: [],
search: null,
select: null,
states: [
'Alabama',
'Alaska',
'American Samoa',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Ohio',
'Oklahoma',
'Oregon',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
]
}),
watch: {
search (val) {
val && val !== this.select && this.querySelections(val)
}
},
methods: {
querySelections (v) {
this.loading = true
// Simulated ajax query
setTimeout(() => {
this.items = this.states.filter(e => {
return (e || '').toLowerCase().indexOf((v || '').toLowerCase()) > -1
})
this.loading = false
}, 500)
}
},
components: {
VContainer,
VFlex,
VLayout,
VCard,
VImg,
VCarousel,
VAutocomplete
}
}
</script>
The problems I am facing are the following:
How can I lose the teal toolbar and have just one simple single line to use for searching, that will be positioned in the center of the carousel?
Whenever I click on the search bar, I see this: [object Object]. How do I get rid of this?
With my current implementation, every time I'm searching for a state I'm able to successfully find it but the problem is that the carousel background transition effect stops. So for example, If I search for "Oklahoma" I can see the result but the background carousel transition stops. How can this be fixed?
I'll tackle your questions as you've put them.
1: The teal color is specified in your declaration of v-toolbar.
<v-toolbar
dark
color="teal"
>
You can set this color to anything you want, rgb or hex, so if you want it to be transparent you could do this.
<v-toolbar
dark
color="rgba(0,0,0,0)"
>
To specify the position of the toolbar comes down to styling. You can adjust the default vuetify styles but for a single item like this the easiest way to set styling that will override the default is to use inline styling, like this:
<v-toolbar
dark
color="rgba(0,0,0,0)"
style="position:absolute;top:75px;z-index:400;"
>
As you've set the height, you can position the toolbar to center it. The z-index may be necessary to place it above the carousel. In here you could also style the text color, width etc.
2: In your posted code the reason you are getting [object Object] is because the v-autocomplete is trying to iterate through 'items' which is empty. You have 'states'. Set items like this: :items="states" To get this working as expected I set it up like this:
<v-autocomplete
style="background:rgba(0,0,0,0)"
v-model="select"
:items="states"
:loading="isLoading"
:search-input.sync="search"
color="white"
hide-no-data
hide-selected
item-text="Description"
item-value="state"
label="States"
placeholder="Start typing to Search"
prepend-icon="mdi-database-search"
return-object
>
</v-autocomplete>
Which is straight from the vuetify docs.
3: I can't reproduce the carousel pausing, it continues throughout on mine.
Hope this helps somewhat.

toLowerCase localeCompare does not work in NuxtJs VueJs Framework

I have encounter a very strange issue in NuxtJs (VueJs Framework).
I have a code which was working well for my stores list in alphabetical order and search filter. As it was working I copied the same code functionality for my categories. The next day when I click on categories it worked fine but on stores it gave me this two errors:
Cannot read property 'localeCompare' of undefined
Cannot read property 'toLowerCase' of undefined
Not sure what went wrong its the same code on both pages, only the difference in data it loads for stores and categories. If remove both localeCompare & toLowerCase it loads the data properly but without the search filter and the list unordered. Below is my code:
<script>
import axios from "axios";
export default {
asyncData({ req, params }) {
return axios.get(process.env.apiURL + "stores").then(res => {
return {
stores: res.data
};
});
},
data() {
return {
apiURL: process.env.apiURL,
active: null,
search: "",
Searched: "",
filterStores: [],
storeList: ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
}
},
methods: {
filterStore(n) {
this.tab = n;
axios.get(process.env.apiURL + 'stores?filter[where][store_name][regexp]=^\\b' + this.tab + '/i').then(res => {
return this.filterStores = res.data;
});
}
},
computed: {
AllStores: function() {
return this.stores.filter(store => {
return store.store_name.toLowerCase().includes(this.search.toLowerCase())
})
}
},
head: {
title: "Stores"
}
};
</script>
<template>
<v-container grid-list-md text-xs-center>
<v-text-field append-icon="search" v-model="search" id="filter" name="filter"
label="Search Your Favourite Store . . . ." auto-grow autofocus dont-fill-mask-blanks solo>
</v-text-field>
<br/>
<v-tabs grow dark color="teal accent-4" show-arrows slider-color="white">
<v-tab active>#</v-tab>
<v-tab v-for="n in storeList" :key="n" ripple #click="filterStore(n)">{{ n }}</v-tab>
<v-tab-item>
<v-layout row wrap>
<v-flex v-for="(store, index) in AllStores" :key="index" xs6 sm4 md2 lg2 xl3>
<v-card light>
<nuxt-link :to="'/store/'+store.store_name">
<v-card-media :src="`${apiURL}containers` + `${store.store_image}`" height="80px"></v-card-media>
<v-card-text class="blue">{{store.store_name}}</v-card-text>
</nuxt-link>
</v-card>
</v-flex>
</v-layout>
</v-tab-item>
</v-tabs>
<br/>
<v-layout row wrap>
<v-flex v-if="filterStores != ''" v-for="(store, index) in filterStores" :key="index" xs6 sm4 md2 lg2 xl3>
<v-card light>
<nuxt-link :to="'/store/'+store.store_name">
<v-card-media :src="`${apiURL}containers` + `${store.store_image}`" height="80px"></v-card-media>
<v-card-text class="blue">{{store.store_name}}</v-card-text>
</nuxt-link>
</v-card>
</v-flex>
<v-flex v-else>No Stores Available starting with this letter</v-flex>
</v-layout>
</v-container>
</template>
I tried to update my vue cli and reinstalled the modules and it worked, thanks

Categories

Resources