I am using Nuxt and vue2-google-maps but I do have the following error
module error: google is not defined
I read the official GitHub FAQ, so I used this.$gmapApiPromiseLazy().then().
But, I do have the aforementioned error.
getCurrentPositionandBathroom method is to get current position and search for convenience store around current position.
<template>
<v-app>
<v-btn class="blue white--text" #click="getCurrentPositionandBathroom"> search for bathroom! </v-btn>
<GmapMap
ref="mapRef"
:center="maplocation"
:zoom="15"
map-type-id="roadmap"
style="height: 300px; width: 900px"
>
<GmapMarker
v-for="m in markers"
:key="m.id"
:position="m.position"
:clickable="true"
:draggable="true"
:icon="m.icon"
#click="center = m.position"
/>
</GmapMap>
</v-app>
</template>
<script>
export default {
data() {
return {
maplocation: { lat: 35.6814366, lng: 139.767157 },
markers: [],
}
},
methods: {
getCurrentPositionandBathroom() {
if (process.client) {
if (!navigator.geolocation) {
alert('Japanese sentences')
return
}
navigator.geolocation.getCurrentPosition(this.success, this.error)
}
},
success(position) {
this.maplocation.lat = position.coords.latitude
this.maplocation.lng = position.coords.longitude
this.$gmapApiPromiseLazy().then(() => {
google.maps.event.addListenerOnce(
this.$refs.mapRef.$mapObject,
'idle',
function () {
this.getBathroom()
}.bind(this),
)
})
},
getBathroom() {
const map = this.$refs.mapRef.$mapObject
const placeService = new google.maps.places.PlacesService(map)
placeService.nearbySearch(
{
location: new google.maps.LatLng(this.maplocation.lat, this.maplocation.lng),
radius: 500,
type: ['convenience_store'],
},
function (results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
results.forEach((place) => {
const icon = {
url: place.icon,
scaledSize: new google.maps.Size(30, 30),
}
const marker = {
position: place.geometry.location,
icon,
title: place.name,
id: place.place_id,
}
this.markers.push(marker)
})
}
}.bind(this),
)
},
error(errorMessage) {
switch (errorMessage.code) {
case 1:
alert('Japanese sentences')
break
case 2:
alert('Japanese sentences')
break
case 3:
alert('Japanese sentences')
break
default:
alert('Japanese sentences')
break
}
},
},
}
</script>
What I should I do?
PS: I can see the Google Maps. In other words, Google Maps is displayed.
Alright, so there was quite a few configuration to do but I achieved to have a working map. Your this.getBathroom() method was not working for me, but this is related to the API or how you handle the logic I guess.
I basically followed the package README and it all went smooth at the end. Nothing special and google is available as explained in the following section:
If you need to gain access to the google object
Here is the final code of the .vue file
<template>
<div>
<button class="blue white--text" #click="getCurrentPositionandBathroom">
search for bathroom!
</button>
<GmapMap
ref="mapRef"
:center="maplocation"
:zoom="15"
map-type-id="roadmap"
style="height: 300px; width: 900px"
>
<GmapMarker
v-for="m in markers"
:key="m.id"
:position="m.position"
:clickable="true"
:draggable="true"
:icon="m.icon"
#click="center = m.position"
/>
</GmapMap>
</div>
</template>
<script>
import { gmapApi } from 'vue2-google-maps'
export default {
data() {
return {
maplocation: { lat: 35.6814366, lng: 139.767157 },
markers: [],
}
},
computed: {
google: gmapApi,
},
methods: {
getCurrentPositionandBathroom() {
if (process.client) {
if (!navigator.geolocation) {
alert('Japanese sentences')
return
}
navigator.geolocation.getCurrentPosition(this.success, this.error)
}
},
success(position) {
this.maplocation.lat = position.coords.latitude
this.maplocation.lng = position.coords.longitude
// this.$gmapApiPromiseLazy().then(() => { // not needed here anymore
this.google.maps.event.addListenerOnce(
this.$refs.mapRef.$mapObject,
'idle',
function () {
this.getBathroom()
}.bind(this)
)
// })
},
getBathroom() {
const map = this.$refs.mapRef.$mapObject
const placeService = new this.google.maps.places.PlacesService(map)
placeService.nearbySearch(
{
location: new this.google.maps.LatLng(
this.maplocation.lat,
this.maplocation.lng
),
radius: 500,
type: ['convenience_store'],
},
function (results, status) {
if (status === this.google.maps.places.PlacesServiceStatus.OK) {
results.forEach((place) => {
const icon = {
url: place.icon,
scaledSize: new this.google.maps.Size(30, 30),
}
const marker = {
position: place.geometry.location,
icon,
title: place.name,
id: place.place_id,
}
this.markers.push(marker)
})
}
}.bind(this)
)
},
error(errorMessage) {
switch (errorMessage.code) {
case 1:
alert('Japanese sentences')
break
case 2:
alert('Japanese sentences')
break
case 3:
alert('Japanese sentences')
break
default:
alert('Japanese sentences')
break
}
},
},
}
</script>
You can find the useful commit on my github repo here.
This is how it looks at the end, no errors so far.
PS: I didn't saw that you were using Vuetify, so I didn't bother bringing it back later on.
Related
I am developing an instantsearch using nuxt. Googlemaps receives the first set on results on page load and renders the markers without any issues - however when searching the new results do not update on the map? Is there a way i can push the new set of results through to the map to render the markers?
I assume this will be the beforeMount hook and pass the results to updateMap?
pages/index.vue
<template>
<div class="root">
<div class="container">
<ais-instant-search-ssr
:search-client="searchClient"
index-name="dive_buddy"
>
<ais-configure :hits-per-page.camel="7">
<ais-search-box placeholder="Search here…" class="searchbox" />
<ais-stats />
<div class="h-screen">
<ais-hits>
<template #default="{ items }">
<div class="grid grid-cols-2 gap-2">
<div v-if="items.length">
<div
v-for="item in items"
:key="item.objectID"
class="my-2 p-2 border"
>
<SiteRow
:site="item"
#mouseover.native="highlightMarker(item.objectID, true)"
#mouseout.native="highlightMarker(item.objectID, false)"
/>
</div>
</div>
<div v-else>No results found</div>
<Map
:items="items"
:lat="items[0].lat"
:lng="items[0].lng"
/>
</div>
</template>
</ais-hits>
<ais-pagination />
</div>
</ais-configure>
</ais-instant-search-ssr>
</div>
</div>
</template>
<script>
import {
AisInstantSearchSsr,
AisConfigure,
AisHits,
AisSearchBox,
AisStats,
AisPagination,
createServerRootMixin,
} from 'vue-instantsearch'
import algoliasearch from 'algoliasearch/lite'
import _renderToString from 'vue-server-renderer/basic'
function renderToString(app) {
return new Promise((resolve, reject) => {
_renderToString(app, (err, res) => {
if (err) reject(err)
resolve(res)
})
})
}
const searchClient = algoliasearch(
'xxx', // AppID
'xxxxxxxx' // APIKey
)
export default {
components: {
AisInstantSearchSsr,
AisConfigure,
AisHits,
AisSearchBox,
AisStats,
AisPagination,
},
mixins: [
createServerRootMixin({
searchClient,
indexName: 'dive_buddy',
}),
],
data() {
return {
searchClient,
}
},
head() {
return {
link: [
{
rel: 'stylesheet',
href: 'https://cdn.jsdelivr.net/npm/instantsearch.css#7.4.5/themes/satellite-min.css',
},
],
}
},
serverPrefetch() {
return this.instantsearch
.findResultsState({
component: this,
renderToString,
})
.then((algoliaState) => {
this.$ssrContext.nuxt.algoliaState = algoliaState
})
},
beforeMount() {
const results =
(this.$nuxt.context && this.$nuxt.context.nuxtState.algoliaState) ||
window.__NUXT__.algoliaState
this.instantsearch.hydrate(results)
// Remove the SSR state so it can't be applied again by mistake
delete this.$nuxt.context.nuxtState.algoliaState
delete window.__NUXT__.algoliaState
},
methods: {
highlightMarker(id, isHighlighted) {
document
.getElementsByClassName(`id-${id}`)[0]
?.classList?.toggle('marker-highlight', isHighlighted)
},
},
}
</script>
components/Map.vue
<template>
<div ref="map" class="h-screen"></div>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
lat: {
type: Number,
required: true,
},
lng: {
type: Number,
required: true,
},
},
mounted() {
this.updateMap()
},
methods: {
// run showMap function in maps.client.js
async updateMap() {
await this.$maps.showMap(
this.$refs.map,
this.lat,
this.lng,
this.getMarkers()
)
},
// pass through markers to googlemaps
getMarkers() {
return this.items.map((item) => {
return {
...item,
}
})
},
},
}
</script>
plugins/maps.client.js
export default function(context, inject){
let isLoaded = false
let waiting = []
addScript()
inject('maps', {
showMap,
})
function addScript(){
const script = document.createElement('script')
script.src = "https://maps.googleapis.com/maps/api/js?key=xxxx&libraries=places&callback=initGoogleMaps"
script.async = true
window.initGoogleMaps = initGoogleMaps
document.head.appendChild(script)
}
function initGoogleMaps(){
isLoaded = true
waiting.forEach((item) => {
if(typeof item.fn === 'function'){
item.fn(...item.arguments)
}
})
waiting = []
}
function showMap(canvas, lat, lng, markers){
if(!isLoaded){
waiting.push({
fn: showMap,
arguments,
})
return
}
const mapOptions = {
zoom: 18,
center: new window.google.maps.LatLng(lat, lng),
disableDefaultUI: true,
zoomControl: true,
styles:[{
featureType: 'poi.business',
elementType: 'labels.icon',
stylers:[{ visibility: 'off' }]
}]
}
const map = new window.google.maps.Map(canvas, mapOptions)
if(!markers){
const position = new window.google.maps.LatLng(lat, lng)
const marker = new window.google.maps.Marker({
position,
clickable: false,
})
marker.setMap(map)
return
}
const bounds = new window.google.maps.LatLngBounds()
let index = 1
markers.forEach((item) => {
const position = new window.google.maps.LatLng(item.lat, item.lng)
const marker = new window.google.maps.Marker({
position,
label: {
text: `${index}`,
color: 'black',
className: `marker id-${item.objectID}`
},
icon: 'https://maps.gstatic.com/mapfiles/transparent.png',
clickable: false,
})
marker.setMap(map)
bounds.extend(position)
index++
})
map.fitBounds(bounds)
}
}
EDIT
As per #Bryan's suggestion - I have previously tried to follow algolia's documentation using vue-googlemaps plugin but this does not work on Nuxt as the map does not load as well as these errors -
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'g')
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'h')
Although this isnt a solution to the above - I have found a workaround using a different library: vue2-google-maps as this works with Nuxt (see docs).
pages/index.vue
<template>
<div class="root">
<div class="container">
<ais-instant-search-ssr
:search-client="searchClient"
index-name="dive_centres"
>
<ais-configure :hits-per-page.camel="7">
<ais-search-box placeholder="Search here…" class="searchbox" />
<ais-stats />
<div class="h-screen">
<ais-hits>
<template #default="{ items }">
<div class="grid grid-cols-2 gap-2">
<div v-if="items.length">
<div
v-for="item in items"
:key="item.objectID"
class="my-2 p-2 border"
>
<SiteRow
:site="item"
#mouseover.native="highlightMarker(item.objectID, true)"
#mouseout.native="highlightMarker(item.objectID, false)"
/>
</div>
</div>
<div v-else>No results found</div>
<Map />
</div>
</template>
</ais-hits>
<ais-pagination />
</div>
</ais-configure>
</ais-instant-search-ssr>
</div>
</div>
</template>
<script>
import {
AisInstantSearchSsr,
AisConfigure,
AisHits,
AisSearchBox,
AisStats,
AisPagination,
createServerRootMixin,
} from 'vue-instantsearch'
import algoliasearch from 'algoliasearch/lite'
import _renderToString from 'vue-server-renderer/basic'
function renderToString(app) {
return new Promise((resolve, reject) => {
_renderToString(app, (err, res) => {
if (err) reject(err)
resolve(res)
})
})
}
const searchClient = algoliasearch(
'xxx', // AppID
'xxxxxxxx' // APIKey
)
export default {
components: {
AisInstantSearchSsr,
AisConfigure,
AisHits,
AisSearchBox,
AisStats,
AisPagination,
},
mixins: [
createServerRootMixin({
searchClient,
indexName: 'dive_sites',
}),
],
head() {
return {
link: [
{
rel: 'stylesheet',
href: 'https://cdn.jsdelivr.net/npm/instantsearch.css#7.4.5/themes/satellite-min.css',
},
],
}
},
serverPrefetch() {
return this.instantsearch
.findResultsState({
component: this,
renderToString,
})
.then((algoliaState) => {
this.$ssrContext.nuxt.algoliaState = algoliaState
})
},
beforeMount() {
const results =
(this.$nuxt.context && this.$nuxt.context.nuxtState.algoliaState) ||
window.__NUXT__.algoliaState
this.instantsearch.hydrate(results)
// Remove the SSR state so it can't be applied again by mistake
delete this.$nuxt.context.nuxtState.algoliaState
delete window.__NUXT__.algoliaState
},
methods: {
highlightMarker(id, isHighlighted) {
console.log(id)
document
.getElementsByClassName(`id-${id}`)[0]
?.classList?.toggle('marker-highlight', isHighlighted)
},
},
}
</script>
components/Map.vue
<template>
<div v-if="state" class="ais-GeoSearch">
<GmapMap
:center="center"
:zoom="zoom"
map-type-id="terrain"
class="h-full w-full"
:options="options"
>
<GmapMarker
v-for="item in state.items"
:key="item.objectID"
:position="item._geoloc"
:clickable="false"
:draggable="true"
:icon="{ url: require('~/static/images/dive-site-small.svg') }"
/>
</GmapMap>
</div>
</template>
<script>
import { createWidgetMixin } from 'vue-instantsearch'
import { connectGeoSearch } from 'instantsearch.js/es/connectors'
export default {
mixins: [createWidgetMixin({ connector: connectGeoSearch })],
data() {
return {
zoom: 4,
options: {
disableDefaultUI: true,
},
}
},
computed: {
center() {
return this.state.items.length !== 0
? this.state.items[0]._geoloc
: { lat: 48.864716, lng: 2.349014 }
},
},
}
</script>
Currently i am using buefy autocomplete.But there are a few issues with it.
DepartmentDetail.vue
<template slot-scope="props">
<div class="container is-fluid">
<b-loading :is-full-page="true" :active.sync="this.isLoading"></b-loading>
<b-field label="Business Unit">
<b-autocomplete
:data="dataBusinessUnit"
placeholder="select a business unit..."
field="businessUnit"
:loading="isFetching"
:value="this.objectData.businessUnit"
#typing="getAsyncDataBusinessUnit"
#select="(option) => {updateValue(option.id,'businessUnit')}"
>
<template slot-scope="props">
<div class="container">
<p>
<b>ID:</b>
{{props.option.id}}
</p>
<p>
<b>Description:</b>
{{props.option.description}}
</p>
</div>
</template>
<template slot="empty">No results found</template>
</b-autocomplete>
</b-field>
</div>
</template>
Function that fetches the results based on user input-
getAsyncDataBusinessUnit: debounce(function(name) {
// console.log('getAsyncDataBusinessUnit you typed'+name);
if (!name.length) {
this.dataBusinessUnit = [];
return;
}
this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/${name}`)
.then(response => {
this.dataBusinessUnit = [];
response.forEach(item => {
this.dataBusinessUnit.push(item);
});
})
.catch(error => {
this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
this.isFetching = false;
});
}, 500),
So instead of using the buefy's b-autocomplete i want to create and use my own autocomplete component.
So i went ahead and created my own autocomplete component and called it from the DepartmentDetail vue page like this-
DepartmentDetail.vue
<b-field label="Custom Business Unit ">
<AutoComplete :method="getAsyncDataBusinessUnit" title='businessUnit' :autocompleteData="dataBusinessUnit" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
AutoComplete.vue
<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="this.objectData[this.title]" #input="getAsyncDataBusinessUnit"/>
<ul v-show="isFetching" >
<li v-for="(dataBusinessUnit, i) in dataBusinessUnit" :key="i" #click="setResult(dataBusinessUnit)" >
<!-- {{ autocompleteData }} -->
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>Description:</b>
{{dataBusinessUnit.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>First Name:</b>
{{dataBusinessUnit.firstName}}
</p>
<p>
<b>Last Name:</b>
{{dataBusinessUnit.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
import schemaData from '../store/schema';
import debounce from "lodash/debounce";
import api from "../store/api";
const ViewName = "AutoComplete";
var passedview;
export default {
name: "AutoComplete",
props: {
method: {
type: Function
},
title: String,
viewname:String,
autocompleteData: {
type: Array,
required: true
}
},
data() {
return {
// results: [],
dataBusinessUnit: [],
isFetching: false
// vignesh: this.objectData[this.title]
};
},
computed: {
viewData() {
return this.$store.getters.getViewData('DepartmentDetail')
},
objectData() {
return this.$store.getters.getApiData(this.viewData.api_id).data
},
sessionData() {
return this.$store.getters.getSessionData()
},
isLoading() {
return this.$store.getters.getApiData(this.viewData.api_id).isLoading
},
newRecord() {
return this.$route.params.id === null;
},
getTitle() {
return this.title
}
},
mounted() {
},
methods: {
setResult(result) {
this.updateValue(result.id,this.title);
// localStorage.setItem(this.title,result.id );
this.isFetching = false;
},
updateValue(newValue, fieldName) {
var val;
var schema = schemaData[this.viewData.schema];
if(typeof schema!=='undefined' && schema['properties'][fieldName]['type'] == 'date'){
val = this.formatDate(newValue);
} else {
val = newValue;
}
this.$store.dispatch('updateDataObjectField', {
key: this.viewData.api_id,
field: fieldName,
value: val
});
},
getAsyncDataBusinessUnit: debounce(function(name) {
console.log('getAsyncDataBusinessUnit you typed'+name.target.value);
if (!name.target.value.length) {
this.dataBusinessUnit = [];
this.isFetching = false;
return;
}
// this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/${name.target.value}`)
.then(response => {
this.dataBusinessUnit = [];
if (!response.length)
{
console.log('inside if')
this.isFetching = false;
}
else{
console.log('inside else')
response.forEach(item => {
this.dataBusinessUnit.push(item);
});
this.isFetching = true;
}
console.log('length of dataBusinessUnit is '+(this.dataBusinessUnit).length)
console.log('contents of dataBusinessUnit array '+JSON.stringify(this.dataBusinessUnit))
})
.catch(error => {
this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
// this.isFetching = true;
});
}, 500),
},
components: {
}
};
</script>
The issue iam facing is whenever i start to type anything into the Custom Business Unit input field, then immediately after 1-2 seconds the value gets reset(with the value coming from the store getters ).This however does not happen if i remove the line this.dataBusinessUnit = []; in the getAsyncDataBusinessUnit function . Why is this happening? I even tried using :input instead of v-model for the input field , but i am still facing the same issue.And also second issue is when i click an existing record under DepartmentDetail page, the value that should be set for the custom business unit input field coming from the store getters (this.objectData.businessUnit) is not showing up sometimes? please help
The basic logic of an autocomplete is not really hard:
Vue.component('Autocomplete', {
props: ['list'],
data() {
return {
input: null
}
},
template: `<div><input v-model="input" #input="handleInput"><div class="bordered" v-if="input"><ul><li v-for="(item, i) in list" :key="i" #click="setInput(item)">{{ item }}</li></ul></div></div>`,
methods: {
handleInput(e) {
this.$emit('input', e.target.value)
},
setInput(value) {
this.input = value
this.$emit('input', value)
}
}
})
new Vue({
el: "#app",
computed: {
filteredList() {
if (this.filterInput) {
return this.list.filter(e => e.toLowerCase().indexOf(this.filterInput.toLowerCase()) !== -1)
} else {
return this.list
}
}
},
data: {
filterInput: null,
list: [
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Seventh"
]
},
methods: {
handleInput(e) {
this.filterInput = e
}
}
})
.bordered {
border: 1px solid black;
display: block;
}
ul li {
cursor: pointer;
}
ul li:hover {
background: rgba(0, 0, 0, 0.3)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<autocomplete :list="filteredList" #input="handleInput" />
</div>
I wouldn't handle the data in the presentational component (autocomplete is a presentational component in the structure I created), but in its parent-container. That way the autocomplete just gets a list as a prop and displays it; every action is $emitted to the parent, who does the data-handling.
It's easier to control displayed data this way - even with an async data source.
How to close autocomplete dropdown when clicked on any outside area? Currently i am calling my custom autocomplete component(child presentation component) 3 times with different labels from another vue page(parent) called departmentDetail.vue.
Example:
departmentDetail.vue
<b-field label="Custom Business Unit ">
<AutoComplete :method="getAsyncDataBusinessUnit" title='businessUnit' :autocompleteData="dataBusinessUnit" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
<b-field label="Custom Managers">
<AutoComplete :method="getAsyncData" title='manager' :autocompleteData="dataManager" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
<b-field label="Custom Location">
<AutoComplete :method=" getAsyncDataLocation" title='location' :autocompleteData="dataLocation" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
AutoComplete.vue (Custom component created by me)
<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="objectData[title]" #focus="getAsyncDataBusinessUnit" #input="getAsyncDataBusinessUnit"/>
<ul v-show="isFetching" >
<li v-for="(dataBusinessUnit, i) in dataBusinessUnit" :key="i" #click="setResult(dataBusinessUnit)" >
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>Description:</b>
{{dataBusinessUnit.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>First Name:</b>
{{dataBusinessUnit.firstName}}
</p>
<p>
<b>Last Name:</b>
{{dataBusinessUnit.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
import schemaData from '../store/schema';
import debounce from "lodash/debounce";
import api from "../store/api";
const ViewName = "AutoComplete";
var passedview;
export default {
name: "AutoComplete",
props: {
method: {
type: Function
},
title: String,
viewname:String,
autocompleteData: {
type: Array,
required: true
}
},
data() {
return {
// results: [],
dataBusinessUnit: [],
results: [],
isFetching: false
// vignesh: this.objectData[this.title]
};
},
computed: {
viewData() {
return this.$store.getters.getViewData('DepartmentDetail')
},
objectData() {
return this.$store.getters.getApiData(this.viewData.api_id).data
},
sessionData() {
return this.$store.getters.getSessionData()
},
isLoading() {
return this.$store.getters.getApiData(this.viewData.api_id).isLoading
},
newRecord() {
return this.$route.params.id === null;
},
getTitle() {
return this.title
}
},
mounted() {
},
methods: {
setResult(result) {
this.updateValue(result.id,this.title);
// localStorage.setItem(this.title,result.id );
this.isFetching = false;
},
updateValue(newValue, fieldName) {
var val;
var schema = schemaData[this.viewData.schema];
if(typeof schema!=='undefined' && schema['properties'][fieldName]['type'] == 'date'){
val = this.formatDate(newValue);
} else {
val = newValue;
}
this.$store.dispatch('updateDataObjectField', {
key: this.viewData.api_id,
field: fieldName,
value: val
});
},
getAsyncDataBusinessUnit: debounce(function(name) {
console.log('getAsyncDataBusinessUnit you typed'+name.target.value);
if (!name.target.value.length) {
// this.results = [];
// this.dataBusinessUnit = [...this.results];
// this.isFetching = false;
// return;
}
// this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/`,{ filter: `{id}like'%${name.target.value}%'` })
.then(response => {
this.results = [];
if (!response.length)
{
console.log('inside if ')
this.isFetching = false;
}
else{
console.log('inside else')
response.forEach(item => {
this.results.push(item);
});
// this.dataBusinessUnit=this.results
this.dataBusinessUnit = [...this.results];
this.isFetching = true;
}
console.log('length of dataBusinessUnit is '+(this.dataBusinessUnit).length)
console.log('contents of dataBusinessUnit array '+JSON.stringify(this.dataBusinessUnit))
})
.catch(error => {
//this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
// this.isFetching = true;
});
}, 500),
},
components: {
}
};
</script>
Screenshot of the Department screen
And why is it that when the page loads sometimes the values dont show up in these input fields? But upon focus or if i type anything then sudddenly the value shows up?Any idea why this is happening?
About your last question:
"And why is it that when the page loads sometimes the values dont show up in these input fields? But upon focus or if i type anything then sudddenly the value shows up?Any idea why this is happening?"
It looks like you are usesing API requests on computed props.
Computed props are pre renderd values. If your API works async then the computed is renderd "empty" before the full request is resolved.
you could try data props and set them with the API setter in Mounted() or Created().
EDIT:
It could look something like this:
data() {
return {
// results: [],
dataBusinessUnit: [],
results: [],
viewData: [],
objectData:[],
sessionData:[],
isLoading: [],
isFetching: false
// vignesh: this.objectData[this.title]
};
},
computed: {
newRecord() {
return this.$route.params.id === null;
},
getTitle() {
return this.title
}
},
mounted() {
this.viewData = this.$store.getters.getViewData('DepartmentDetail');
this.objectData = this.$store.getters.getApiData(this.viewData.api_id).data;
this.sessionData = this.$store.getters.getSessionData();
this.isLoading = this.$store.getters.getApiData(this.viewData.api_id).isLoading;
},
I using this tutorial to use Google maps API https://markus.oberlehner.net/blog/using-the-google-maps-api-with-vue/ and it is working well. But it use this.$el to load the Google map instance inside the component and that makes take all screen, I want to load inside .map instead so I can have smaller map.
<template>
<div class="container-fluid">
init map
<div class="map">
</div>
</div>
</template>
<script>
import ProgressSpinner from '.././Preloader';
import { mapGetters } from 'vuex'
import gmapsInit from '../.././utils/gmaps';
export default {
name: 'Tracking',
data: () => ({
}),
async mounted() {
$('.container-fluid').bootstrapMaterialDesign();
try {
const google = await gmapsInit();
const geocoder = new google.maps.Geocoder();
const map = new google.maps.Map(this.$el);
geocoder.geocode({ address: 'Austria' }, (results, status) => {
if (status !== 'OK' || !results[0]) {
throw new Error(status);
}
map.setCenter(results[0].geometry.location);
map.fitBounds(results[0].geometry.viewport);
});
} catch (error) {
console.error('Error with calling Google maps API: ',error);
}
},
computed: {
...mapGetters([])
},
created(){
this.$store.dispatch ('allPoints')
.then(()=> console.log('points'))
.catch( () => console.log('no poiints ') )
},
methods: {
},
components: {
ProgressSpinner
}
}
</script>
<style scoped>
.map {
width: 150px;
}
</style>
The problem with style, so I should have added height to the .map element.
I've searched the web for hours now and can't seem to find the problem to my seemingly, pretty simple issue.
Simply put, the icon property of google.maps.Marker doesn't seem to do anything when I ionic serve the app, despite everything else working out fine.
In other words, what does Ionic 2 use with the Google Maps Javascript API that allows it to define the icon images for custom markers?
I'll provide all my relevant code here but I have a feeling that it might not be very helpful for a question like this.
With what I know about Ionic 2, I've been able to integrate Google Maps, it's online/offline states, and some default markers into a page on my app.
BTW, my test image files are located in the same folder as google-maps.ts (just doing this for now as I figure out what's happening).
All the code for initializing google maps and creating the addMarker functions are located in this one file (This huge piece of code is placed here just in case, skip the code below to the next snippet to see the most relevant section of it):
src/providers/google-maps.ts
import { Injectable } from '#angular/core';
import { Connectivity } from './connectivity';
import { Geolocation } from 'ionic-native';
/*
Generated class for the GoogleMaps provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
declare var google;
#Injectable()
export class GoogleMaps {
mapElement: any;
pleaseConnect: any;
map: any;
mapInitialised: boolean = false;
mapLoaded: any;
mapLoadedObserver: any;
markers: any = [];
apiKey: string;
styles: any;
constructor(public connectivityService: Connectivity) {
}
init(mapElement: any, pleaseConnect: any): Promise<any> {
this.mapElement = mapElement;
this.pleaseConnect = pleaseConnect;
return this.loadGoogleMaps();
}
loadGoogleMaps(): Promise<any> {
return new Promise((resolve) => {
if(typeof google == "undefined" || typeof google.maps == "undefined") {
console.log("Google maps Javascript needs to be loaded");
this.disableMap();
if(this.connectivityService.isOnline()) {
window['mapInit'] = () => {
this.initMap().then(() => {
resolve(true);
});
this.enableMap();
}
let script = document.createElement("script");
script.id = "googleMaps";
if(this.apiKey) {
script.src = 'http://maps.google.com/maps/api/js?key=' + this.apiKey
+ '&callback=mapInit';
} else {
script.src = 'http://maps.google.com/maps/api/js?callback=mapInit';
}
document.body.appendChild(script);
}
}
else {
if(this.connectivityService.isOnline()) {
this.initMap();
this.enableMap();
} else {
this.disableMap();
}
}
this.addConnectivityListeners();
})
}
initMap(): Promise<any> {
this.mapInitialised = true;
return new Promise((resolve) => {
Geolocation.getCurrentPosition().then((position) => {
let latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
let mapOptions = {
center: latLng,
zoom: 15,
//mapTypeId: google.maps.MapTypeId.ROADMAP, -Doesn't seem necessary anymore
styles: [
{
"featureType": "poi.business",
"stylers": [
{
"visibility": "off"
}
]
},
{
"featureType": "road",
"elementType": "labels.icon",
"stylers": [
{
"visibility": "off"
}
]
},
{
"featureType": "transit",
"stylers": [
{
"visibility": "off"
}
]
}
]
}
this.map = new google.maps.Map(this.mapElement, mapOptions);
resolve(true);
});
});
}
disableMap(): void {
if(this.pleaseConnect) {
this.pleaseConnect.style.display = "block";
}
}
enableMap(): void {
if(this.pleaseConnect) {
this.pleaseConnect.style.display = "none";
}
}
addConnectivityListeners(): void {
document.addEventListener('online', () => {
console.log("online");
setTimeout(() => {
if(typeof google == "undefined" || typeof google.maps == "undefined") {
this.loadGoogleMaps();
}
else {
if(!this.mapInitialised) {
this.initMap();
}
this.enableMap();
}
},2000);
}, false);
}
//Setting up custom Google Maps markers
//iconBase: any = 'https://maps.google.com/mapfiles/kml/shapes/'; -Probably not necessary
icons: any = {
partner: {
icon: 'partner.png'
},
boughtFrom: {
icon: 'boughtFrom.png'
}
}
addMarker(lat: number, lng: number, feature: any): void {
let latLng = new google.maps.LatLng(lat, lng);
let marker = new google.maps.Marker({
map: this.map,
animation: google.maps.Animation.DROP,
position: latLng,
icon: this.icons[feature].icon
});
this.markers.push(marker);
}
}
The part that isn't working for me is the "icon" assignment in that last "addMarker()" function:
addMarker(lat: number, lng: number, feature: any): void {
let latLng = new google.maps.LatLng(lat, lng);
let marker = new google.maps.Marker({
map: this.map,
animation: google.maps.Animation.DROP,
position: latLng,
icon: this.icons[feature].icon //Doesn't do anything
});
this.markers.push(marker);
}
Currently I'm attempting to also call different types of markers for different locations, but even if I simply replace it with partners.png or { url: 'partners.img' }, it still doesn't recognize anything.
In case this matters, these are also the two test markers I'm using that appear in default style on the map:
src/assets/data/locations.json
{
"locations": [
{
"latitude": 40.79567309999999,
"longitude": -73.97358559999998,
"type": "partner"
},
{
"latitude": 40.8107211,
"longitude": -73.95413259999998,
"type": "boughtFrom"
}
]
}
I'll also include the map page that integrates all this info:
src/pages/home.ts
import { Component, ElementRef, ViewChild } from '#angular/core';
import { Locations } from '../../providers/locations';
import { GoogleMaps } from '../../providers/google-maps';
import { NavController, Platform } from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
#ViewChild('map') mapElement: ElementRef;
#ViewChild('pleaseConnect') pleaseConnect: ElementRef;
constructor(public navCtrl: NavController, public maps: GoogleMaps,
public Platform: Platform, public locations: Locations) {
}
ionViewDidLoad() {
this.Platform.ready().then(() => {
let mapLoaded = this.maps.init(this.mapElement.nativeElement, this.pleaseConnect.nativeElement);
let locationsLoaded = this.locations.load();
Promise.all([
mapLoaded,
locationsLoaded
]).then((result) => {
let locations = result[1];
for(let location of locations) {
this.maps.addMarker(location.latitude, location.longitude, location.type);
}
})
});
}
}
Thank you for your time!
Any and all help is appreciated.
In other words, what does Ionic 2 use with the Google Maps Javascript API that allows it to define the icon images for custom markers?
Nothing. Ionic has no responsibility for this whatsoever.
BTW, my test image files are located in the same folder as google-maps.ts (just doing this for now as I figure out what's happening).
This is the issue. The build process takes typescript files from a place and compiles them in to a single file. In to build www/build/main.js.
These images are not there with the main.js
Move your images to assets folder and give the proper path.
For example:
icon: 'assets/icon1.png'