I have following part in my html file.
<script src="http://maps.googleapis.com/maps/api/js?key=AIzaSyDwsTgxJyu7KG7BPM3q54mgjSHl1imLnCM&sensor=false">
</script>
<script type="text/javascript">
$(function () {
require(["Work/AddCiniViewModel"], function (model) {
addCiniViewModel = new model();
addCiniViewModel.createMap();
ko.applyBindings(addCiniViewModel, document.getElementById("AddCiniForm"));
}
});
I have following part in my ViewModel file.
self.map;
self.Lng = ko.observable(12.24);
self.Lat = ko.observable(24.54);
self.createMap = function () {
var elevator;
var myOptions = {
zoom: 3,
center: new google.maps.LatLng(12.24, 24.54),
mapTypeId: 'terrain'
};
map = new google.maps.Map($('#map')[0], myOptions);
}
ko.bindingHandlers.map = {
init: function (element, valueAccessor, allBindingsAccessor, addCiniViewModel) {
var position = new google.maps.LatLng(allBindingsAccessor().latitude(),
allBindingsAccessor().longitude());
var marker = new google.maps.Marker({
map: allBindingsAccessor().map,
position: position,
title: name
});
self._mapMarker = marker;
},
update: function (element, valueAccessor, allBindingsAccessor, addCiniViewModel) {
var latlng = new google.maps.LatLng(allBindingsAccessor().latitude(), allBindingsAccessor().longitude());
self._mapMarker.setPosition(latlng);
}
};
And I would like to see google maps and its lat long values in the following div.
<div id="map"></div>
<input data-bind="value: Lat" />
<input data-bind="value: Lng" />
<div data-bind=" style: style:{width:'300px',height:'300px'},
latitude: addCiniViewModel.Lat, longitude:addCiniViewModel.Lng, map:map">
</div>
When I run this code. I got this message in firebug.
google.maps.LatLng is not a constructor
What's wrong? How can i solve this problem?
The Google Maps API javascript is loading asynchronously while execution of your code is continuing. Your code then uses the Maps API before it is available.
To wait for the Maps API you can wrap your ko.applyBindings call inside a jQuery onload event handler
$(window).load(function() {
ko.applyBindings(addCiniViewModel, document.getElementById("AddCiniForm"));
});
Here is another example from Google on how to wait for the maps API before initializing code that uses it: https://developers.google.com/maps/documentation/javascript/tutorial
Related
I'm using the Google Maps API v3.30 to display multiple GeoJSON map layers. I have declared each layer separately, and I am able to load the GeoJSON, set styles, and switch between each layer without any issue. However when I try to use the forEach method on the declared layer, any code within is not run.
var myLayer = new google.maps.Data();
myLayer.loadGeoJson('Content/Maps/myGeoJsonData.geojson');
myLayer.setStyle( function (feature) {return defaultStyle(feature); });
myLayer.setMap(map);
myLayer.forEach(function (feature) { console.log("Test"); }); // Doesn't work
Everything above works except for the forEach method. I've tried rearranging the order of each line but still the only line that doesn't run is the forEach
loadGeoJson is asynchronous. You need to wait until it completes loading the data from the request before calling .forEach. Use its optional callback function:
from the documentation:
loadGeoJson(url:string, options?:Data.GeoJsonOptions, callback?:function(Array))
Return Value: None
Loads GeoJSON from a URL, and adds the features to the collection.
var myLayer = new google.maps.Data();
myLayer.loadGeoJson(
'Content/Maps/myGeoJsonData.geojson', {},
function(features) {
console.log("loadGeoJson callback "+features.length);
myLayer.forEach(function(feature) {
console.log("Test");
});
});
myLayer.setStyle( function (feature) {return defaultStyle(feature); });
myLayer.setMap(map);
proof of concept fiddle
code snippet:
function initialize() {
var map = new google.maps.Map(
document.getElementById("map_canvas"), {
zoom: 4,
center: {
lat: -28,
lng: 137
},
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var myLayer = new google.maps.Data();
myLayer.loadGeoJson(
'https://storage.googleapis.com/mapsdevsite/json/google.json', {},
function(features) {
console.log("loadGeoJson callback " + features.length);
myLayer.forEach(function(feature) {
console.log("Test");
});
});
myLayer.setMap(map);
}
google.maps.event.addDomListener(window, "load", initialize);
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas"></div>
I tried using this code here: fiddle
But it keeps sending me a javascript error "Uncaught TypeError: Cannot read property 'x' of undefined".
I mostly think it because the element didn't have time to load before the init function was executed.
that's my view:
<div id="mapDiv" data-bind="map:myMap"></div>
that's my binding:
ko.bindingHandlers.map = {
init: function (element, valueAccessor, allBindingsAccessor) {
var mapObj = ko.utils.unwrapObservable(valueAccessor());
var latLng = new google.maps.LatLng(
ko.utils.unwrapObservable(mapObj.lat),
ko.utils.unwrapObservable(mapObj.lng));
var mapOptions = { center: latLng,
zoom: 15};
mapObj.googleMap = new google.maps.Map(element, mapOptions);
mapObj.marker = new google.maps.Marker({
map: mapObj.googleMap,
position: latLng,
title: "You Are Here",
});
$("#" + element.id).data("mapObj",mapObj);
}
};
and that's my viewmodel:
module.activate = function(settings){
var self = this;
this.myMap = ko.observable({
lat: 34.1,
lng: 31.6});
this.compositionComplete = function(child, parent, settings){
};
};
return module;
});
I made a fiddle with your code, and once I styled the map div to have height and width, it ran without a problem.
<div id="mapDiv" style="width:300px; height:300px" data-bind="map:myMap"></div>
I never did see any reference to x, so I can't say I've addressed your issue, but if you think you have something running before Google Maps is ready, see this solution for adding a listener for the idle event.
How is it possible to use the Google Maps API with AngularJS?
I am using this code in my AngularJS app:
<style>
#googleMap {
width: 200px;
height: 200px;
}
</style>
<div id="googleMap"></div>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js"></script>
<script language="javascript">
var map;
function initialize() {
var mapOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP,
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644)
};
map = new google.maps.Map(document.getElementById('googleMap'), mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
This code is in the view and not in the controller.
I have other functions in the view and they work, but google map is still not working.
This is the error I get in the browser console:
Error: google is not defined
#http://localhost:3000/js/jquery-1.11.2.min.js line 2 > eval:10:2
.globalEval/<#http://localhost:3000/js/jquery-1.11.2.min.js:2:2615
.globalEval#http://localhost:3000/js/jquery-1.11.2.min.js:2:2589
.domManip#http://localhost:3000/js/jquery-1.11.2.min.js:3:23105
.after#http://localhost:3000/js/jquery-1.11.2.min.js:3:21067
Cehttp://localhost:3000/js/angular/lib/angular.min.js:176:70
n#http://localhost:3000/js/angular/lib/angular-route-segment.min.js:7:5868
.compile/http://localhost:3000/js/angular/lib/angular-route-segment.min.js:7:6428
Pe/this.$gethttp://localhost:3000/js/angular/lib/angular.min.js:128:120
p#http://localhost:3000/js/angular/lib/angular-route-segment.min.js:7:2649
this.$gethttp://localhost:3000/js/angular/lib/angular-route-segment.min.js:7:3989
f/<#http://localhost:3000/js/angular/lib/angular.min.js:112:20
Pe/this.$gethttp://localhost:3000/js/angular/lib/angular.min.js:125:301
Pe/this.$gethttp://localhost:3000/js/angular/lib/angular.min.js:122:390
Pe/this.$gethttp://localhost:3000/js/angular/lib/angular.min.js:126:56
l#http://localhost:3000/js/angular/lib/angular.min.js:81:169
S#http://localhost:3000/js/angular/lib/angular.min.js:85:301
vf/http://localhost:3000/js/angular/lib/angular.min.js:86:315
What am I doing wrong?
I searched the web but everyone seems to be using libraries like angular-google-map or ui-map. Why is no one using the direct API?
You can implement google maps in angularjs without using any plugins like this,
<!--use this div where ever you want to create a map-->
<div id="map"></div>
define the width and height for map div,
#map {
height:420px;
width:600px;
}
in controller you glue this id="map" with scope like this,
$scope.mapOptions = {
zoom: 4,
center: new google.maps.LatLng(41.923, 12.513),
mapTypeId: google.maps.MapTypeId.TERRAIN
}
$scope.map = new google.maps.Map(document.getElementById('map'), $scope.mapOptions);
if you want to create markers for the cities or countries you want,
var cities = "Atlanta, USA";
var geocoder= new google.maps.Geocoder();
$scope.markers = [];
var createMarker = function (info){
var marker = new google.maps.Marker({
map: $scope.map,
position: new google.maps.LatLng(info.lat(), info.lng())
});
}
geocoder.geocode( { 'address': cities }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
newAddress = results[0].geometry.location;
$scope.map.setCenter(newAddress);
createMarker(newAddress)
}
});
Last but not least make sure you added the google maps api script before doing all this stuff,
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"> </script>
Here is the working plunker with this code, inside a bootstrap model,
http://embed.plnkr.co/VT7cO0L2ckSWG6g63TVG/preview
Reference
Hope this helps!
It was late to post but I have done this as solution. By this there is no need to add the google map script src in the head of the html or there is no chance of any error for google map src related errors. The script will be added automatically by the loadScript function. In angular it is needed to add new js src in partials rather than the head of main script. So I think this will be the best solution
I used this code chunk into my controller.
$scope.initialize = function() {
$scope.mapOptions = {
zoom: 8,
center: new google.maps.LatLng(22.649907498685803, 88.36255413913727)
};
$scope.map = new google.maps.Map(document.getElementById('googleMap'), $scope.mapOptions);
}
$scope.loadScript = function() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://maps.google.com/maps/api/js?sensor=false&callback=initialize';
document.body.appendChild(script);
setTimeout(function() {
$scope.initialize();
}, 500);
}
setTimeout is there because some time is needed for google map src to be downloaded and be ready. & callback=initialize is needed because by this google map will be ready for a callback.The main problem was that the code google.maps.event.addDomListener(window, 'load', initialize) was not executing if I add the google map src in partial rather than the head of main index.html. But this setup works flawlessly.
And this chunk to the html
<div class="form-group col-lg-12" id="googleMap">
<center>Waiting for the map...</center>
</div>
Now place ng-init="loadScript()" anywhere in any outer div so that loadScript initializes before.
I am trying to show a simple map in HotTowel.
In home.html page I have that:
<section>
<div id="map-canvas" data-bind="map: map"></div>
</section>
In home.js I have that:
define(['services/logger'], function (logger) {
var vm = {
activate: activate,
title: 'Home View',
map: map
};
return vm;
function activate() {
google.maps.event.addDomListener(window, 'load', initialize);
logger.log('Home View Activated', null, 'home', true);
return true;
}
var map;
function initialize() {
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}
});
How to bind model with view to show map?
EDIT**
The below answer was for Durandal 1.2. In durandal 2.0 the viewAttached event was renamed to attached. You can read Durandals documentation about it here.
Durandal has a viewAttached event that is called on your viewmodel once the view has been databound and attached to the dom. That would be a good place to call the google maps api.
define(['services/logger'], function (logger) {
var vm = {
viewAttached: initialize
title: 'Home View',
map: map
};
return vm;
var map;
function initialize(view) {
logger.log('Home View Activated', null, 'home', true);
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}
});
EDIT AGAIN to address peoples comments**
As per Jamie Hammond's comment it is a better practice to scope your DOM transversal to the view that's being attached. If the DOM element is apart of the view.
So, inside your viewAttached (in durandal 1.2) or attached (in durandal 2.0) you would:
var map;
var mapOptions = { /*map options*/ };
var vm = {
attached: function (view) {
var mapCanvas = $('#map-canvas', view).get(0);
map = new google.maps.Map(mapCanvas, mapOptions);
}
}
I haven't messed with Durandal 2.0 at all because I've been pretty busy with work and stuff and when I was messing around with Durandal 1.0 it was just for fun but I do love the framework and hope to one day get to play with 2.0. With that said I did have an issue with generating a map in the viewattached in Durandal 1.0. But, I wasn't using Google maps. I was using Leafletjs. My solution to the problem was creating a delay in the viewAttached that would redraw the map after a short delay. This was because Durandal's transitioning in the view was not working well with leaflets ability to draw the map in the dom element as it was flying and fading in.
So, inside the viewAttached I would draw the map like so:
window.setTimeout(drawMap, 10);
Again, this was a very specific problem I had and not a problem with Durandal. This was more of a problem with Leafletjs not rendering the map correctly when the DOM element was still transitioning in.
Evan,
I'm also trying to get this working but no joy.
I have my html as and viewmodel exactly as you have, and I know the viewAttached composition is being called because I'm getting my logger event - but no map!
The only other thing I can think of is where you call your googlemaps from? I'm doing in in my index.html are you doing the same?
Regards
BrettH,
For me, the problem was that the height was 0px. My module that works looks like this:
define(['plugins/router', 'knockout', 'plugins/utility'], function (router, ko, utility) {
var vm = { };
vm.map = undefined;
vm.compositionComplete = function () {
var myLatlng = new google.maps.LatLng(29.4000, 69.1833);
var mapOptions = {
zoom: 6,
center: myLatlng
}
vm.map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var georssLayer = new google.maps.KmlLayer({
url: 'http://www.visualtravelguide.com/Pakistan-Islamabad.kmz'
});
georssLayer.setMap(vm.map);
utility.resizeElementHeight(document.getElementById('map-canvas'), 10);
$(window).resize(function () {
utility.resizeElementHeight(document.getElementById('map-canvas'), 10);
});
};
return vm;
});
My utility module looks like this:
define(['jquery','knockout'], function ($,ko) {
return {
//resizes an element so height goes to bottom of screen, got this from a stack overflow
//usage:
// resizeElementHeight(document.getElementById('projectSelectDiv'));
//$(window).resize(function () {
// resizeElementHeight(document.getElementById('projectSelectDiv'));
//});
//adjustpixels is an optional parameter if you want to leave room at the bottom
resizeElementHeight: function (element,adjustPixels) {
var height = 0;
var adjust = 0;
if (adjustPixels != undefined)
adjust = adjustPixels;
var body = window.document.body;
if (window.innerHeight) {
height = window.innerHeight;
} else if (body.parentElement.clientHeight) {
height = body.parentElement.clientHeight;
} else if (body && body.clientHeight) {
height = body.clientHeight;
}
element.style.height = ((height - element.offsetTop-adjust) + "px");
},
//looks up name by id, returns blank string if not found
//pass in a list and an id (they can be observables)
LookupNameById: function (l, wId) {
var list = ko.utils.unwrapObservable(l);
var id = ko.utils.unwrapObservable(wId);
var name = '';
$.each(list, function (key, value) {
if (value.Id() == id)
name = value.Name();
});
return name;
},
//sets the widths of the columns of headertable to those of basetable
setHeaderTableWidth: function (headertableid,basetableid) {
$("#"+headertableid).width($("#"+basetableid).width());
$("#"+headertableid+" tr th").each(function (i) {
$(this).width($($("#"+basetableid+" tr:first td")[i]).width());
});
$("#" + headertableid + " tr td").each(function (i) {
$(this).width($($("#" + basetableid + " tr:first td")[i]).width());
});
}
};
});
Hope this helps you.
first go to your main.js and add 'async': '../Scripts/async',
require.config({
paths: {
'text': '../Scripts/text',
'durandal': '../Scripts/durandal',
'plugins': '../Scripts/durandal/plugins',
'mapping': '../Scripts/knockout.mapping-latest',
'async': '../Scripts/async',
'transitions': '../Scripts/durandal/transitions'
},
shim: { mapping: { deps: ['knockout'] } }
});
notice that we need to add async.js in the scripts folder so go to Download async.js download the file and save it in hottowel script folder as async.js
the in the main.js add this
// convert Google Maps into an AMD module
define('gmaps', ['async!http://maps.google.com/maps/api/js?v=3&sensor=false'],
function(){
// return the gmaps namespace for brevity
return window.google.maps;
});
in any viewmodel you can now use it like this
define(['plugins/router', 'knockout', 'services/logger', 'durandal/app', 'gmaps'], function (router, ko, logger, app, gmaps) {
i hope this will help:
We're building a google map app in rails that initially loads some makers using a javascript json marker generating function (using the rails .to_json method on the data object).
Then we have a listener on the zoom action that hoovers the new json file directly and feeds it into the same marker function above.
On initial load the markers turn up fine, but on zoom no new ones seem to be showing up. Checking in the rails logs, the json file is being called, so the problem is either to do with how that json data is processed, or how the markers are delivered.
Can any of you see what the problem is?
var map;
function initialize() {
var myOptions = {
zoom: <%= #zoom %>,
center: new google.maps.LatLng(<%= #centre %>),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map_canvas'),
myOptions);
stream_json = (<%= raw(#stream.to_json) %>);
parse_json(stream_json);
google.maps.event.addListener(map, 'zoom_changed', function() {
json_url = "/test?bounds="+map.getBounds()+"&zoom="+map.getZoom();
stream_json = $.getJSON(json_url);
parse_json(stream_json);
});
function parse_json(json) {
if (json.length > 0) {
var markers = [];
for (i=0; i<json.length; i++) {
var place = json[i];
alert(place.longitude +','+place.latitude);
// addLocation(place);
markers[i] = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(place.latitude, place.longitude)
});
}
}
};
}
google.maps.event.addDomListener(window, 'load', initialize);
Many thanks in advance for any pointers you can send our way!
You're not using getJSON correctly. It's an asynchronous call, so you need to supply a callback:
google.maps.event.addListener(map, 'zoom_changed', function() {
json_url = "/test?bounds="+map.getBounds()+"&zoom="+map.getZoom();
$.getJSON(json_url, function(data) {
parse_json(data);
});
});