angularjs googlemap not loading when trying to load in a modal - angularjs

I am using AngularJS google maps to display map with markers.
The scenario is :
Initially Contact Details like Door no, Street, Area etc fields page will be displayed along with a static map. Once an edit button is clicked a pop-up with all the fields and map is displayed.
ex:
CODE:
html
<div class="row" ng-controller="userProfileController">
<ui-gmap-google-map center="center1" zoom="zoom1" pan="true" events="events">
<ui-gmap-markers models="models1" coords="'self'" options="'options'"></ui-gmap-markers>
</ui-gmap-google-map>
</div>
controller
$scope.center1 = {
latitude: lat,
longitude: lng
};
$scope.zoom1 = 8;
$scope.models1 = [{
id: 11445522,
latitude: lat,
longitude: lng,
options: {
title: "Current Location"
}
}];
Well everything works fine so far.
When edit is clicked i am trying to load another html in the modal that contains fields and a map. This time the map isn't loading. if I press 'F12' then map can be seen.
Code for popup:
html
<div class="col-sm-12">
<ui-gmap-google-map center="center3" zoom="zoom3" pan="true" events="events3" refresh="true">
<ui-gmap-markers doRebuildAll="true" doCluster="true" models="models3" coords="'self'" options="'options'"></ui-gmap-markers>
</ui-gmap-google-map>
controller
$scope.center3 = {latitude: 19.20742852680121,
longitude: 73.553466796875
};
$scope.zoom3 = 7;
$scope.models3 = [{
id: 5421222,
latitude: 19.20742852680121,
longitude: 73.553466796875,
options: {
title: "User Location"
}
}];
What might be the issue.? Can someone help me?
It displays like this:

I had a similar problem, but in my case, the user input an address and return the location. I found on the Internet the solution, and with some adjustments, I decided this way...
First, I create myController in app.js
app.controller('myController'), function ($scope) {
// my variable that's control my modal
$scope.showModal = false;
// my click event, like your 'Edit' button
$scope.createModal = function () {
$scope.showModal = true;
};
}
HTML index.html
<my-modal visible="showModal"></my-modal>
HTML modal.html
<div class="form-group">
<input type="text" class="form-control" ng-model="chosenPlace" details="chosenPlaceDetails" googleplace placeholder="Address"/>
<div class="map_container">
<div id="map_canvas" class="map_canvas"></div>
</div>
</div>
Then, in my modal.js, I created two Directive's
// Directive of Google Maps
angular.module('modal', [])
.directive('googleplace', function () {
return {
require: 'ngModel',
scope: {
ngModel: '=',
details: '=?'
},
controller: function ($scope) {
$scope.gPlace;
$scope.map;
$scope.marker;
$scope.initMap = function () {
// Set the initial lat and lng
var latlng = new google.maps.LatLng(-20.00, -47.00);
var options = {
zoom: 5,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
$scope.map = new google.maps.Map($("#map_canvas")[0], options);
$scope.marker = new google.maps.Marker({
map: $scope.map,
draggable: true,
});
$scope.marker.setPosition(latlng);
};
$scope.initMap();
},
link: function(scope, element, attrs, model) {
var options = {
types: ['geocode'],
componentRestrictions: { country: 'us' }
};
scope.gPlace = new google.maps.places.Autocomplete(element[0], options);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
google.maps.event.trigger(scope.map, 'resize');
var location = new google.maps.LatLng(scope.gPlace.getPlace().geometry.location.A, scope.gPlace.getPlace().geometry.location.F);
scope.marker.setPosition(location);
scope.map.setCenter(location);
scope.map.setZoom(16);
});
});
}
};
});
.directive('myModal', function () {
return {
templateUrl: 'modal.html',
restrict: 'E',
replace: true,
scope: true,
controller: function ($scope) {
},
link: function postLink(scope, element, attrs) {
scope.$watch(attrs.visible, function(value) {
if(value == true) {
$(element).modal('show');
}
else {
$(element).modal('hide');
}
});
$(element).on('shown.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function(event){
scope.$apply(function(){
scope.$parent[attrs.visible] = false;
});
});
}
};
});
When the user write his address, and hit Enter, the listener on the map, find the address and marker on the map.
I did like that because it was the best solution I found for my project.
I hope that helps.
PS: Sorry my english :/

Related

Adding a marker using a controller to a Google Maps initialised in a directive

I have a Google Maps with options instantiated inside a directive :
.directive('uiMap',
['uiMapConfig', '$parse', function (uiMapConfig, $parse) {
var mapEvents = 'bounds_changed center_changed click dblclick drag dragend ' +
'dragstart heading_changed idle maptypeid_changed mousemove mouseout ' +
'mouseover projection_changed resize rightclick tilesloaded tilt_changed ' +
'zoom_changed';
var options = uiMapConfig || {};
return {
restrict: 'A',
//doesn't work as E for unknown reason
link: function (scope, elm, attrs) {
var opts = angular.extend({}, options, scope.$eval(attrs.uiOptions));
var map = new window.google.maps.Map(elm[0], opts);
var model = $parse(attrs.uiMap);
//Set scope variable for the map
model.assign(scope, map);
bindMapEvents(scope, mapEvents, map, elm);
}
};
}]);
The map is displayed correctly thanks to this div :
<section id="map">
<div ui-map="myMap" ui-options="ctrl.mapOptions" class="google-map"></div>
</section>
What I am trying to do is to add a marker on this map after clicking on a button using a controller:
this.findAddress = function() {
var myLatlng = new google.maps.LatLng(-25.363882,131.044922);
var marker = new google.maps.Marker({
position: myLatlng,
title:"Hello World!"
});
marker.setMap(map);
}
The problem is that the "map" is undefined in the controller and I don't know how to access to the instance of the map.
Thank you for your help.
From uiMap directive you could notify the controller once the map is created via $emit:
scope.$emit('mapReady', map);
In controller
$scope.map = null;
$scope.$on('mapReady', function (event, map) {
$scope.map = map;
});
and
$scope.findAddress = function () {
//create a marker here...
marker.setMap($scope.map);
}
Example
angular.module('myApp', [])
.directive('uiMap',
['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, elm, attrs) {
var opts = scope.$eval(attrs.uiOptions);
var map = new window.google.maps.Map(elm[0], opts);
scope.$emit('mapReady', map);
}
};
}])
.controller('MapCtrl', [
'$scope', function ($scope) {
$scope.map = null;
$scope.mapOptions = {
zoom: 12,
center: new google.maps.LatLng(-33.870501, 151.206704),
mapTypeId: google.maps.MapTypeId.TERRAIN
};
$scope.isMapReady = function(){
return $scope.map != null;
}
$scope.$on('mapReady', function (event, map) {
$scope.map = map;
});
$scope.findAddress = function () {
var myLatlng = new google.maps.LatLng(-33.870501, 151.206704);
var marker = new google.maps.Marker({
position: myLatlng,
title: "Hello World!"
});
marker.setMap($scope.map);
}
}]);
.google-map {
width: 640px;
height: 320px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script src="https://maps.google.com/maps/api/js"></script>
<script src="script.js"></script>
<div ng-app="myApp">
<div ng-controller="MapCtrl">
<button ng-click="findAddress()" ng-disabled="!isMapReady()">Find address</button>
<section id="map">
<div ui-map="myMap" ui-options="{{mapOptions}}" class="google-map"></div>
</section>
</div>
</div>

AngularJS ng-click not firing using angular-google-maps' windows

I wanted to automatically center the map to the user's location once loaded so I used $scope.$apply() once geolocation is loaded as seen in my TestCtrl in controller.js here:
$scope.drawMap = function(position) {
$scope.$apply(function() {
$scope.myLocation.lng = position.coords.longitude;
$scope.myLocation.lat = position.coords.latitude;
$scope.map = {
center: {
latitude: $scope.myLocation.lat,
longitude: $scope.myLocation.lng
},
zoom: 14,
events: {
click: $scope.clickCallback
}
};
});
};
navigator.geolocation.getCurrentPosition($scope.drawMap);
$scope.test = function(){
alert("hola");
};
The $scope.clickCallback is used to push new markers to the map on click event.
// inside TestCtrl
var markers = [], counter = 1;
$scope.clickCallback = function(map, eventName, event){
var lat = event[0].latLng.lat();
var lng = event[0].latLng.lng();
markers.push(createNewMarker(counter, lat, lng));
$scope.$apply(function(){
$scope.newMarker = markers;
});
counter++;
};
As you can see, there's another $scope.$apply there to apply the new marker/s.
The createNewMarker() is where the markers (ui-gmap-markers) models is defined.
// still inside TestCtrl
var createNewMarker = function(i, lat, lng, idKey) {
if (idKey == null) {
idKey = "id";
}
var foo = "<h4>New Marker</h4><form><input type='text' placeholder='Event name' name='name'></input> <input type='button' value='submit'></input><input type='button' ng-click='test()' value='Delete marker'></input></form>";
var bar = $compile(foo)($scope);
var ret = {
latitude: lat,
longitude: lng,
show: true,
options: {
draggable: true,
animation: google.maps.Animation.DROP,
},
windows: {
title: "New Marker",
},
windowsOptions: {
content: foo,
}
};
ret[idKey] = i;
return ret;
};
Now the marker is showing fine when I click on the map including the window, but when I click on the Delete marker button, my $scope.test() function isn't firing up. I tried using $compile but it returns a bunch of error about $scope.
Here's my template:
<ion-content scroll="false">
<ui-gmap-google-map center='map.center' zoom='map.zoom' bounds="map.bounds" events="map.events">
<ui-gmap-markers models="newMarker" coords="'self'" icon="'icon'" options="'options'">
<ui-gmap-windows show="show" options="'windowsOptions'">
</ui-gmap-windows>
</ui-gmap-markers>
</ui-gmap-google-map>
</ion-content>
Anyone familiar with this scenario? I'm using Ionic Framework btw.
It does not have to be this complex.
As a creator of ngMap, I would recommend this,
http://ngmap.github.io/drawings.html#/marker-remove#marker-remove
To set the current location, just use current-location
<map center="current-location" zoom="12" on-click="addMarker()">
<marker ng-repeat="pos in positions" position="{{pos.lat}}, {{pos.lng}}"></marker>
</map>
http://plnkr.co/edit/e1SioHQ6NTSYCp0EbR0x?p=preview

How to call function in directive from button click

How do I call a function in a directive from a button click? I have been trying and have come up with this (but it is not working):
HTML
<div ng-controller="myMapCTRL as myMapctrl">
<div id="panel">
<input ng-click="updateMap()" type=button value="Remove Path">
</div>
<my-map-with-path id="map-canvas" class="map-canvas" ng-if="dataHasLoaded" ></my-map-with-path>
</div>
Controller
app.controller('myMapCTRL', ['$scope', 'PathService', function($scope, PathService){
//console.log('in controller');
$scope.removed = false;
if(typeof $scope.paths ==='undefined') {
$scope.dataHasLoaded = false;
$scope.center = new google.maps.LatLng(51.5130300, -0.3202410);
PathService.getPaths().then(function(data){
$scope.paths = data;
$scope.dataHasLoaded = true;
//console.log('paths loaded');
});
};
}]);
Directive
app.directive('myMapWithPath', [function() {
return{
restrict: 'AE',
template: '<div></div>',
replace: true,
controller: 'myMapCTRL',
link: function(scope, element, attrs){
//console.log('in link');
scope.updateMap = function() {
console.log('inside updateMap()');
}
var map, path = new google.maps.MVCArray(),
service = new google.maps.DirectionsService(), poly;
//var center = new google.maps.LatLng(51.5130300, -0.3202410);
var myOptions = {
zoom: 15,
center: scope.center,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControlOptions: {
mapTypeIds: [google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.HYBRID,
google.maps.MapTypeId.SATELLITE]
},
disableDoubleClickZoom: true,
scrollwheel: false,
draggableCursor: "crosshair"
}
map = new google.maps.Map(document.getElementById("map-canvas"), myOptions);
poly = new google.maps.Polyline({ map: map });
for(var i = 0; i < scope.paths['j'].length; i++) {
var lat = scope.paths['j'][i]['k']
var lng = scope.paths['j'][i]['D']
var lat_lng = new google.maps.LatLng(lat, lng);
path.push(lat_lng);
}
poly.setPath(path);
google.maps.event.addListener(map, "click", function(evt) {
if (path.getLength() === 0) {
path.push(evt.latLng);
poly.setPath(path);
} else {
service.route({
origin: path.getAt(path.getLength() - 1),
destination: evt.latLng,
travelMode: google.maps.DirectionsTravelMode.DRIVING
}, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
for (var i = 0, len = result.routes[0].overview_path.length;
i < len; i++) {
path.push(result.routes[0].overview_path[i]);
}
}
});
}
//console.log(path);
});
}
}
}]);
I want to call scope.updateMap from the button click but it is not firing in the console.
This won't work because the ng-click is outside the directive.
You should move the function updateMap to the $scope of myMapCTRL
Having dug around a little more, it seems quite normal to use a shared service to communicate between a controller and a directive.
The general idea is this:
HTML
<div ng-controller="myMapCTRL as myMapctrl">
<div id="panel">
<input ng-click="updateMap()" type=button value="Remove Path">
</div>
<my-map-with-path id="map-canvas" class="map-canvas" ng-if="dataHasLoaded" ></my-map-with-path>
</div>
SharedService
app.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.doSomething = function() {
$rootScope.$broadcast('messageBroadcast');
};
return sharedService;
});
Controller
app.controller('myMapCTRL', ['$scope', 'mySharedService',
function($scope, sharedService){
$scope.updateMap = function() {
sharedService.doSomething();
}
}]);
Directive
app.directive('myMapWithPath', [function() {
return{
restrict: 'AE',
template: '<div></div>',
replace: true,
controller: 'myMapCTRL',
link: function(scope, element, attrs){
scope.$on('messageBroadcast', function() {
console.log('in directive broadcast message');
});
...
}
}
}]);
The idea seems to be that the controller calls a function in the shared service which "broadcasts" a message out. The directive waits for that message and when it is received, it does something amazing.
I am not sure if I need to inject the shared service into the directive or link function but it seems to work without it.

How do I add markers to a Google Map?

I am trying to have an input field that when submitted adds a marker to my Google Map. Right now when I submit the field it is creating the object but the marker is not being displayed. Right now I am able to get a location to show up if it is hard coded in but not when I add a new one. (I know that the view right now is only for the hard coded one, I have that so the current code is working)
Here is my code:
My View:
<form>
<input type="number" class="" ng-model="marker.markerLat" required="">
<input type="number" class="" ng-model="marker.markerLng" required="">
<button class="button" ng-click="addMarker(marker)">Add</button>
</form>
<google-map center="map.center" zoom="map.zoom">
<marker coords="marker.coords" options="marker.options" idkey="marker.id" >
</marker>
</google-map>
My Controller:
//Default location
$scope.map = {
center: {
latitude: 32.7833,
longitude: -79.9333
},
zoom: 11
}
$scope.options = {scrollwheel: true};
$scope.markers = [];
$scope.addMarker = function (marker) {
$scope.markers.push({
latitude: parseFloat($scope.markerLat),
longitude: parseFloat($scope.markerLng)
});
console.log('Maker add: ' + $scope.markers);
$scope.markerLat ="";
$scope.markerLng ="";
};
$scope.marker = {
id:0,
coords: {
latitude: 32.7833,
longitude: -79.9333
}
}
I would advice you to create a custom angular directive for your map.
But anyway, angular is not enough to get what you want working. You have to create google.maps objects. And set the map property of your marker to the map you have created.
Here is a little example :
.directive('map', function () {
return {
template: '<div></div>',
restrict: 'EA',
replace: true,
link: function (scope, element) {
scope.markers = [];
scope.map = new google.maps.Map(element[0], {
center: new google.maps.LatLng(32.7833, -79.9333),
zoom: 11
});
scope.addMarker = function (lat, lnt) {
var marker = new google.maps.Marker({
map: scope.map,
position: new google.maps.LatLng(lat, lng)
});
scope.markers.push(marker);
};
}
});
So you simply have to call the addMarker function with a lat and lng parameter. Use angular events to communicate between your controller and directive. More info about the methods here

The "with" binding of KnockoutJS in AngularJS?

I have just switched from KnockoutJS to AngularJS and I am not able to find the KnockoutJS's "with" data-bind in AngularJS.
Here is the piece of code in KnockoutJS. The "with" binding creates a new binding context, so that descendant elements are bound in the context of a specified object.
<h1 data-bind="text: city"> </h1>
<p data-bind="with: coords">
Latitude: <span data-bind="text: latitude"> </span>,
Longitude: <span data-bind="text: longitude"> </span>
</p>
<script type="text/javascript">
ko.applyBindings({
city: "London",
coords: {
latitude: 51.5001524,
longitude: -0.1262362
}
});
</script>
Does AngularJS have anything like context?
Nothing like with that I know of.. this is the best I could do:
<h1>{{city}}</h1>
<p ng-repeat="c in [coords.or.possibly.deeper.in.tree]">
Latitude: {{c.latitude}},
Longitude: {{c.longitude}}
</p>
Create a custom directive that loops through the source object and creates corresponding properties on the directive's scope that are getter/setter references to the source object.
Check out this plunker.
directive module:
angular.module('koWith', [])
.directive('koWith', function () {
return {
controller: function ($scope, $attrs) {
var withObj = $scope.$parent[$attrs.ngWith];
function getter(prop) {
return this[prop];
}
function setter(val, prop) {
this[prop] = val;
}
for (var prop in withObj) {
if (withObj.hasOwnProperty(prop)) {
Object.defineProperty($scope, prop, {
enumerable: true,
configurable: true,
get: getter.bind(withObj, prop),
set: setter.bind(withObj, prop)
});
}
}
},
restrict: 'A',
scope: true
};
});
app module:
angular.module('myApp', [])
.controller('myController', function ($scope) {
$scope.customer = {
name: "Timmeh",
address: {
address1: "12 S Street",
address2: "",
city: "South Park",
state: "CO",
zipCode: "80440"
}
};
});
html:
<div ko-with="customer">
<h2>{{name}}</h2>
<div ko-with="address">
{{address1}}<br>
{{address2}}<br>
{{city}}, {{state}} {{zipCode}}
</div>
</div>
Explanation
In KnockoutJS, bindings keep the bindingContext and data separated so creating the with binding is trivial since it only needs to create a new child bindingContext from the current one and use the with object as its data value.
In AngularJS, a directive's scope is basically the bindingContext and data object rolled into one. When a new scope is created, in order to get the with-like behavior, the properties of the with object have to be referenced onto the newly created scope object.
Here is solution based on #nwayve, but it supports expressions in koWith and also it watches for updating property/expression specified in koWith:
.directive('koWith', function () {
return {
restrict: 'A',
scope: true,
controller: function ($scope, $attrs, $parse) {
var ScopePropertyDesc = function (prop) {
var self = this;
self.propName = prop;
self.parsed = $parse(prop);
self.enumerable = true;
self.configurable = true;
//self.writable = true;
self.get = function () {
var withObj = $scope.$parent[$attrs.koWith];
var res = self.parsed($scope.$parent, withObj);
return res;
};
self.set = function (newValue) {
var withObj = $scope.$parent[$attrs.koWith];
self.parsed.assign(withObj, newValue);
};
};
$scope.$parent.$watch($attrs.koWith, function (oldVal, newVal) {
var withObj = $scope.$parent[$attrs.koWith];
(function copyPropertiesToScope(withObj) {
for (var prop in withObj) {
if (withObj.hasOwnProperty(prop)) {
Object.defineProperty($scope, prop, new ScopePropertyDesc(prop));
}
};
})(withObj);
});
}
};
});

Resources