I'm attempting to use this Google Maps for AngularJS directive inside another function. The code works when I move $scope.map outside the function call and set the lat/lon statically. However, what I want to do is set the lat/lon dynamically within my function call.
Code below.
html:
<google-map center="map.center" zoom="map.zoom"></google-map>
Angular controller:
angular.module('StadiumCtrl', []).controller('StadiumController', function($scope, $rootScope, $routeParams, $sce, Stadia, Instagram, Weather) {
// pull stadium details from API based on routeParams
$scope.id = $routeParams.id;
Stadia.get($scope.id).success(function(response) {
$rootScope.logo = response.logo;
$scope.homeColors = {
"border": "2px solid " + response.sec_hex,
"box-shadow": "3px 3px 7px " + response.prim_hex,
"margin": "6px",
"padding": "0"
}
$scope.map = {
center: {
latitude: response.loc[1],
longitude: response.loc[0]
},
zoom: 8
};
// pass loc_id into Instagram API call
Instagram.get(response.loc_id).success(function(response) {
instagramSuccess(response.loc_id, response);
});
// pass city and state into Wunderground API call
Weather.get(response.city, response.state).success(function(response) {
$rootScope.temp = Math.round(response.current_observation.temp_f);
});
// Instagram API callback
var instagramSuccess = function(scope,res) {
if (res.meta.code !== 200) {
scope.error = res.meta.error_type + ' | ' + res.meta.error_message;
return;
}
if (res.data.length > 0) {
$scope.items = res.data;
} else {
scope.error = "This location has returned no results";
}
};
});
It looks like the google-maps directive needs an initial value when the directive gets linked. Once there is a default value it will respond to changes in that scope value.
You can see this in action with the following code:
$scope.map = {center: {latitude:0,longitude:0}, zoom: 0};
$timeout(function() {
$scope.map = {center: {latitude: 51.219053, longitude: 4.404418 }, zoom: 14 };
}, 1000);
which is demonstrated at http://plnkr.co/edit/szhdpx2AFeDevnUQQVFe?p=preview. If you comment out the $scope.map assignment outside the $timeout the map will no longer show up, but if you put line 3 back it in will update the map when the $timeout executes.
In your case, you should simply be able to add
$scope.map = {
center: {
latitude: 0,
longitude: 0
},
zoom: 0
};
just before you run Stadia.get($scope.id).success(function(response) {.
Related
Trying to implement google map into Vue component. But having a hard time. Actually, there is no error. But no map also :) Okay, what I tried so far down below.
In laravel blade I set my api.
<script async defer src="https://maps.googleapis.com/maps/api/js?key={{env('GOOGLE_MAPS_API')}}&callback=initMap"></script>
Then in Vue component;
data() {
return {
mapName: "map",
//some other codes
}
},
mounted() {
this.fetchEstates();
},
methods: {
fetchEstates(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
// console.log(response);
this.estates = response.data.data;
//some other codes....
//some other codes....
},
computed: {
//some other functions in computed...
//
initMap: function(){
var options =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), options);
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: this.estates.price,
color: "#fff",
},
position: {
lat: this.estates.lat,
lng: this.estates.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
}
<div id="map"></div>
and last marker url id bind is in controller like this,
public function details($id)
{
$estates = allestates::where('id', $id)->first();
return view('pages.details', compact('estates'));
}
Do I missing something in Vue js? Thank you!
From our discussion in the comments, I realise that your issue is because this.estates is still not defined when initMap() is executed. Remember that you are using an asynchronous operation (via axios) to populate this.estates, so it is undefined at runtime. What you can do is:
Keep the map initialisation logic in initMap()
Move all the Google Map marker creation until after the axios promise has been resolved. You can abstract all that into another method, e.g. insertMarkers()
Also, remember that you need to define estates in the app/component data, otherwise it will not be reactive.
Here is an example:
data() {
return {
mapName: "map",
// Create the estate object first, otherwise it will not be reactive
estates: {}
}
},
mounted() {
this.fetchEstates();
this.initMap();
},
methods: {
fetchEstates: function(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
this.estates = response.data.data;
// Once estates have been populated, we can insert markers
this.insertMarkers();
//pagination and stuff...
});
},
// Iniitialize map without creating markers
initMap: function(){
var mapOptions =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), mapOptions);
},
// Helper method to insert markers
insertMarkers: function() {
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: this.estates.price,
color: "#fff",
},
position: {
lat: this.estates.lat,
lng: this.estates.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
}
},
Update: It also turns out that you have not addressed the issue of the data structure of this.estates. It appears that you are receiving an array from your endpoint instead of objects, so this.estates will return an array, and of course this.estates.lat will be undefined.
If you want to iterate through the entire array, you will have to use this.estates.forEach() to go through each individual estates while adding the marker, i.e.:
data() {
return {
mapName: "map",
// Create the estate object first, otherwise it will not be reactive
estates: {}
}
},
mounted() {
this.fetchEstates();
this.initMap();
},
methods: {
fetchEstates: function(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
this.estates = response.data.data;
// Once estates have been populated, we can insert markers
this.insertMarkers();
//pagination and stuff...
});
},
// Iniitialize map without creating markers
initMap: function(){
var mapOptions =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), mapOptions);
},
// Helper method to insert markers
insertMarkers: function() {
// Iterate through each individual estate
// Each estate will create a new marker
this.estates.forEach(estate => {
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: estate.price,
color: "#fff",
},
position: {
lat: estate.lat,
lng: estate.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
});
}
},
From what I can see in the screenshot you posted, this.estates is an array of objects? If that's the case you need to iterate through the array using forEach
this.estates.forEach((estate, index) => {
console.log(estate.lat);
//handle each estate object here
});
or use the first item in the array like so this.estates[0].lat, if you're only interested in the first item.
I am trying to create dynamic markers to load information from my json file. For some reason, the json data never loads. When I try to load one marker, it works fine without the json data. I don't see what the error is. In the console, it says "TypeError: (intermediate value).error is not a function". Here is the code below.
html script link
<script src="https://maps.googleapis.com/maps/api/js?CLIENT ID HERE
&v=3.21&callback=initMap"
async defer></script>
External JS
var map;
function initMap() {
var myLatlng = {
lat: -25.363,
lng: 131.044
};
var centerZone = {
lat: 0,
lng: 0
};
map = new google.maps.Map(document.getElementById('map'), {
center: centerZone,
zoom: 3,
minZoom: 3
});
$.getJSON('data/data.json', function(data) {
$.each(data.markers, function(i, value) {
var myLatlng = new google.maps.LatLng(value.lat, value.lon);
alert(myLatlng);
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: value.lon
});
});
}.error(function(words) {
alert(words);
}));
var secretMessages = ['This', 'is', 'the', 'secret', 'message'];
/*marker.addListener('click', function() {
map.setZoom(6);
map.setCenter(marker.getPosition());
attachSecretMessage(marker, secretMessages[0]);
});*/
function attachSecretMessage(marker, secretMessage) {
var infowindow = new google.maps.InfoWindow({
content: secretMessage
});
marker.addListener('click', function() {
infowindow.open(marker.get('map'), marker);
});
}
// google.maps.event.addDomListener(window, 'load', initMap);
}
json data
{
"markers": [
{
"id": "1",
"name": "Mesoamerica",
"lat": "-25.363",
"lon": "131.044",
"zoomLevel": "6"
}
]
}
The json data will have more objects inside, this is just a sample of how I want it.
You need to wait until the JSON data loads before doing anything with it. I suggest placing everything that relies on the JSON file in a $.done() function, like this:
$.getJSON('data/data.json').done(function(data){
//everything else
});
Your browser will continue with the other lines of code while it's waiting for the $.getJSON function to return the data. That's why you're getting the "not a function" error; you're trying to call a function on something that doesn't exist and JS doesn't know what to do with it. If you place everything in $.done(), those lines won't execute until the JSON data has successfully been retrieved.
I'm making a little app for training with AngularJS, and I have a problem.
I'm receiving JSON data through an API, and then I display it in a map using leaflet directives for Angular.
The problem is one of the thing I try to display give me an error.
This is my controller :
toulouseVeloControllers.controller('toulouseVeloListCtrl', ['$scope', '$http',
function($scope, $http) {
angular.extend($scope, {
osloCenter: {},
markers: {},
defaults: {
scrollWheelZoom: false
}
});
$http.get('https://api.jcdecaux.com/vls/v1/stations?contract=toulouse&apiKey=*************************************').success(function(data) {
$scope.bornes = data;
$scope.markers=[];
$scope.osloCenter=[];
for (var i = 0; i < data.length; i++) {
$scope.markers[i] = {
lat: data[i].position.lat,
lng: data[i].position.lng,
message: data[i].available_bikes,
focus: false,
draggable: false
};
}
$scope.osloCenter = {
lat: data[10].position.lat,
lng: data[10].position.lng,
zoom: 15
};
});
}]);
And this is my HTML :
<div ng-controller="toulouseVeloListCtrl">
<leaflet markers="markers" center="osloCenter" style="width: 100%; height: 500px;"></leaflet>
</div>
In $scope.markers[i], when I try to show data[i].available_bikes, it give me an error, but if I try to show other like data[i].name or data[i].address, there is no problem.
Anyone can tell me what is wrong here ?
Thank you a lot !
The contents of the popup needs to be a string.
for (var i = 0; i < data.length; i++) {
$scope.markers[i] = {
lat: data[i].position.lat,
lng: data[i].position.lng,
message: "Available Bikes: " + data[i].available_bikes,
focus: false,
draggable: false
};
}
https://jsfiddle.net/tombatossals/4PhzC/
I am using Angular-google-maps, HTML code follows
<ui-gmap-google-map center='mapData.map.center' zoom='mapData.map.zoom'
events="mapEvents">
<ui-gmap-markers models="mapData.map.markers" coords="'self'">
</ui-gmap-markers>
</ui-gmap-google-map>
in JS calling
angular.extend(this, $controller('MapsMixinController',
{$scope:$scope, map:mapData.data[0].map}));
MapsMixinController as follows. Calling this controller from js code. Markers are showing & on click able to mark.
MapsMixinController.js
/**
* Controller providing common behaviour for the other map controllers
*/
angular
.module('app')
.controller('MapsMixinController', ['$scope', 'GeolocationService', 'uiGmapGoogleMapApi', 'map',
function($scope, GeolocationService, GoogleMapApi, map) {
var _this = this;
$scope.mapEvents = {
click: function(mapModel, eventName, originalEventArgs) {
var e = originalEventArgs[0];
if (e.latLng) {
$scope.mapData.map.markers.push({
id: new Date().getTime(),
latitude: e.latLng.lat(),
longitude: e.latLng.lng()
});
// This event is outside angular boundary, hence we need to call $apply here
$scope.$apply();
}
}
};
// Returns a default map based on the position sent as parameter
this.getDefaultMap = function(position) {
return {
markers: [],
center: {
latitude: position.coords.latitude,
longitude: position.coords.longitude
},
zoom: 14
};
};
// Initialize the google maps api and configure the map
GoogleMapApi.then(function() {
GeolocationService().then(function(position) {
$scope.mapData.map = map || _this.getDefaultMap(position);
}, function() {
$scope.error = "Unable to set map data"; // TODO use translate
});
});
}
]);
How can I show title on mouse hover on markers? And on click how to show description on markers?
You can add title property alone with latitude and longtitude property while creating marker data.
/**
* Controller providing common behaviour for the other map controllers
*/
angular
.module('app')
.controller('MapsMixinController', ['$scope', 'GeolocationService', 'uiGmapGoogleMapApi', 'map',
function($scope, GeolocationService, GoogleMapApi, map) {
var _this = this;
$scope.mapEvents = {
click: function(mapModel, eventName, originalEventArgs) {
var e = originalEventArgs[0];
if (e.latLng) {
$scope.mapData.map.markers.push({
id: new Date().getTime(),
latitude: e.latLng.lat(),
longitude: e.latLng.lng(),
title: "Mouse over text"
});
// This event is outside angular boundary, hence we need to call $apply here
$scope.$apply();
}
}
};
// Returns a default map based on the position sent as parameter
this.getDefaultMap = function(position) {
return {
markers: [],
center: {
latitude: position.coords.latitude,
longitude: position.coords.longitude
},
zoom: 14
};
};
// Initialize the google maps api and configure the map
GoogleMapApi.then(function() {
GeolocationService().then(function(position) {
$scope.mapData.map = map || _this.getDefaultMap(position);
}, function() {
$scope.error = "Unable to set map data"; // TODO use translate
});
});
}
]);
My app needs to get a path ( list of latitude and longitudes )in order to display it on a map.
I created a basic controller that does all the api calls.
function mainController($scope, $http){
$http.get('/api/lastrun')
.success(function(data){
$scope.lastrun = data;
})
.error(function(data){
console.log('Error: ' + data);
});
}
lastrun has a path array that lets you access the each position.
I created a mapController using angular-leaf-directive
function mapController($scope, positionService){
angular.extend($scope, {
run: {
lat: 0.0,
lng: 0.0,
zoom: 4
},
path: {
p1: {
color: 'red',
weight: 2,
latlngs: [
{ lat: 51.50, lng: -0.082 }, //here is an example of lat and lng in this controller
{ lat: 48.83, lng: 2.37 },
{ lat: 0, lng: 7.723812 }
]
}
}
});
}
What I want to do seems pretty easy. I just want to put the array of positions I get while calling /api/lastrun into my mapController in latlngs.
I'm not completely familiar with Services in AngularJS, but I tried to built mine (positionService). However it didn't work.
Does anyone here know how I can proceed in order to create with my service an array containing a list of {lat : , lng: } and call it into my mapController ?
I would have done :
$scope.lastrun = [];
$http.get('/api/lastrun')
.success(function(data){
angular.forEach(data, function (value) {
$scope.lastrun.push({lat : value.lat, lng : value.lng});
});
}
})
and then :
path: {
p1: {
color: 'red',
weight: 2,
latlngs: $scope.lastrun
}
Hope this helps
I have finally found a solution. I use Adrien's solution in my service, not in my controller and then return the lastrunpos array to my mapController. Here's my code :
var selftracking = angular.module('selftracking',["leaflet-directive"])
selftracking.service('positionService', function($http){
var lastrunpos = [];
$http.get('/api/lastrun')
.success(function(data){
angular.forEach(data.path, function (value) {
lastrunpos.push({lat : value.latitude, lng : value.longitude});
});
});
return {
getPos : function() {
return lastrunpos;
}
}
});
function mainController($scope, $http){
//Get all the last activities in the front page
$http.get('/api/lastactivity')
.success(function(data){
$scope.lastactivity = data;
})
.error(function(data){
console.log('Error: '+ data);
});
$http.get('/api/lastrun')
.success(function(data){
$scope.lastrun = data;
})
.error(function(data){
console.log('Error: ' + data);
});
}
function mapController($scope, positionService){
angular.extend($scope, {
run: {
lat: 51.505,
lng: -0.09,
zoom: 4
},
path: {
p1: {
color: 'red',
weight: 8,
latlngs: positionService.getPos()
}
}
});
}