getting following error as Cannot set property 'opacity' of undefined
HTML and Js as following
<ui-gmap-window show="map.infoWindow.show" coords="map.infoWindow.center" options="map.infoWindow.options"></ui-gmap-window>
$scope.map.infoWindow.options.content = "<h1>....<div>....</div></h1>";
and got the root cause
we should not use content obj inside the infoWindow Options from
AngularJS Google Map Directive - Error while displaying InfoWindow on Marker Click event
So tried from above stack
<ui-gmap-window show="map.infoWindow.show" coords="map.infoWindow.center" options="map.infoWindow.options">
{{ infoWindowContent }}
</ui-gmap-window>
$scope.infoWindowContent = "<h1>....<div>....</div></h1>";
Here, able to solve that console error. but html is not rendering. Showing Plain html string( Not converting into DOM )
Is there any way to solve this issue?
Since ng-bind-html directive does not seem to work properly with google.maps.InfoWindow, for example setting content property ui-gmap-window directive:
<ui-gmap-window show="infoWindow.show" coords='infoWindow.coords'>
<div ng-bind-html="{{infoWindow.content}}"></div>
</ui-gmap-window>
will cause the error that you have experienced.
But you could consider to introduce a custom directive to display InfoWindow content as html:
.directive('toHtml', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function link($scope, $element, attrs) {
attrs.$observe('toHtml', function (value) {
if (value.length > 0) {
var $el = $compile(value)($scope);
$element.empty().append($el)
}
})
}
}
}])
and then bind html content:
<ui-gmap-window show="infoWindow.show" coords='infoWindow.coords'>
<div to-html="{{infoWindow.content}}"></div>
</ui-gmap-window>
Example
angular.module('MapApp', ['uiGmapgoogle-maps'])
.directive('toHtml', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function link($scope, $element, attrs) {
attrs.$observe('toHtml', function (value) {
if (value.length > 0) {
var $el = $compile(value)($scope);
$element.empty().append($el)
}
})
}
}
}])
.controller('MapCtrl', function ($scope, uiGmapGoogleMapApi, uiGmapIsReady) {
$scope.map = {
zoom: 4,
bounds: {},
center: {
latitude: 40.1451,
longitude: -99.6680
},
options: {}
};
$scope.infoWindow = {
show: false,
content: '',
coords: {}
};
$scope.markers = [
{
latitude: 40.705565,
longitude: -74.1180857,
title: "New York",
id: 1,
},
{
latitude: 37.7576948,
longitude: -122.4726193,
title: "San Fransisco",
id: 2,
}
];
uiGmapGoogleMapApi.then(function (maps) {
$scope.showInfoWindow = function (marker, eventName, model) {
$scope.infoWindow.coords.latitude = model.latitude;
$scope.infoWindow.coords.longitude = model.longitude;
$scope.infoWindow.content = "<h2>" + model.title + "</h2>";
$scope.infoWindow.show = true;
};
$scope.closeInfoWindow = function () {
$scope.infoWindow.show = false;
};
});
});
.angular-google-map-container {
height: 30em;
}
<script src="https://code.angularjs.org/1.3.14/angular.js"></script>
<script src="https://rawgit.com/nmccready/angular-simple-logger/master/dist/angular-simple-logger.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-google-maps/2.2.1/angular-google-maps.js"></script>
<div ng-app="MapApp" ng-controller="MapCtrl">
<ui-gmap-google-map center="map.center" zoom="map.zoom" draggable="false" options="map.options" bounds="map.bounds">
<ui-gmap-window show="infoWindow.show" coords='infoWindow.coords' closeClick="closeInfoWindow()">
<div to-html="{{infoWindow.content}}"></div>
</ui-gmap-window>
<ui-gmap-markers models="markers" coords="'self'" options="'options'" click="showInfoWindow">
</ui-gmap-markers>
</ui-gmap-google-map>
</div>
Related
Whenever one of the regions on the map are clicked, it should open the window for that region, but it doesn't.
The expected results would be as follows:
Here is how I want it to look.
Here is my html:
<ui-gmap-polygon static="true" ng-repeat="p in regions track by p.id" path="p.path" visible="p.visible" stroke="p.stroke" fill="p.fill" events="events" clickable="true" >
</ui-gmap-polygon>
<ui-gmap-windows models="markers" show="show">
<!-- Window Code -->
</ui-gmap-windows>
Here is the $scope.events.click function for the polygons:
for(var i = 0; i < $scope.markers.length; i++) {
//console.log(model.path[0].id + " == " + $scope.markers[i].id);
if(model.path[0].id == $scope.markers[i].id) {
console.log("Showing window " + model.path[0].id);
$scope.markers[i].show = true;
}
else
{ // hides other windows
$scope.markers[i].show = false;
}
}
Something to note. The log statement does show up, just not the window.
Here is a plunker: Plunker
I guess you want to add multiple polygons so change your html to:
<ui-gmap-google-map center="map.center" zoom="map.zoom" draggable="true" options="options" bounds="map.bounds">
<ui-gmap-polygons static="true" models="regions" path="'path'" visible="true" stroke="'stroke'" fill="'fill'" events="events" clickable="true" >
</ui-gmap-polygons>
<ui-gmap-windows models="markers" show="'show'" coords="'coords'">
<div ng-non-bindable>Test</div>
</ui-gmap-windows>
</ui-gmap-google-map>
You need to define the property which will define the value as string e.g.: show="'show'" coords="'coords'"
I added a second model just because i wanted to test it which multiple values. So my controller looks like this:
...
$scope.regions = [
{
id: 0,
path: [
{latitude: 39.13773735160255, longitude: -86.51972115039825, id: 0},
{latitude: 39.137606286024926, longitude: -86.51961386203766},
{latitude: 39.137664537422864, longitude: -86.51949316263199},
{latitude: 39.13779768329436, longitude: -86.51960045099258}
]
},
{
id: 1,
path: [
{latitude: 39.13673735160255, longitude: -86.51972115039825, id: 1},
{latitude: 39.136606286024926, longitude: -86.51961386203766},
{latitude: 39.136664537422864, longitude: -86.51949316263199},
{latitude: 39.13679768329436, longitude: -86.51960045099258}
]
}
]
$scope.markers= [{
id: 0,
show: true,
coords: { //you need an object containing latitude and longitude, so i wrapped these properties into coords
latitude: 39.13773735160255,
longitude: -86.51972115039825
}
},{
id: 1,
show: true,
coords: {
latitude: 39.13673735160255,
longitude: -86.51972115039825
}
}
]
$scope.events = {
click: function(polygon, eventName, model) {
// show the window
for(var i = 0; i < $scope.markers.length; i++) {
if(model.path[0] && model.path[0].id == $scope.markers[i].id) {
$scope.markers[i].show = true;
console.log("Window Displayed")
} else {
$scope.markers[i].show = false;
}
}
}
};
You should change your data models if you can e.g. regions where id is defined twice.
I figured out an alternative solution to the problem. Instead of using a window for every marker, I used just one:
<ui-gmap-window coords="selected" show="windowShown" closeClick="closeClick()">
<!-- Window Code -->
</ui-gmap-window>
Then in the controller we have a similar click function:
/*
Events when clicking on a polygon.
*/
$scope.events = {
click: function(polygon, eventName, model) {
// show the window
for(var i = 0; i < $scope.markers.length; i++) {
if(model.path[0].id == $scope.markers[i].id) {
$scope.selected = $scope.markers[i];
$scope.windowShow = true;
}
}
}
};
Lastly, we have a closeClick function to make sure $scope.windowShown is set to false after the window is closed. Otherwise, you cannot reopen the window on the same polygon after it has been closed.
$scope.closeClick = function() { $scope.windowShow = false; }
I having the diffuculty to create a custom control in order to change drawMode of the map in the Angular Google Maps library.
My markup is this
<ui-gmap-google-map id="map" center="map.center" pan="map.pan" zoom="map.zoom" draggable="true" refresh="map.refresh" options="map.options" events="map.events" bounds="map.bounds" dorebuildall="true">
<ui-gmap-map-control template="js/app/templates/mapToolbar.tpl.html" position="top-right" controller="mapWidgetCtrl"></ui-gmap-map-control>
<ui-gmap-polygons models="map.polygons" clickable="true" draggable="true" editable="true" dorebuildall="true"></ui-gmap-polygons>
<ui-gmap-markers models="mapMarkers" coords="'self'" icon="'icon'" events="clickEventsObject"></ui-gmap-markers>
<ui-gmap-drawing-manager options="drawingManagerOptions" control="drawingManagerControl" events="drawEventHandler"></ui-gmap-drawing-manager>
</ui-gmap-google-map>
And my controllers are these (writing most relevant parts of code)
app.controller('mapSearchCtrl', ["$scope", "$http", function ($scope, $http) {
$scope.map = {
center: {
latitude: 40,
longitude: 20
},
zoom: 9,
bounds: {},
polygons: {},
options: {
panControl: false,
zoomControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_BOTTOM
},
mapTypeControl: false,
disableDefaultUI: true
}
};
$scope.drawingManagerOptions = {
drawingMode: null,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
google.maps.drawing.OverlayType.POLYGON,
]
},
polygonOptions: {
strokeWeight: 3,
editable: true
}
};
$scope.drawingManagerControl = {};
$scope.options = {};
$scope.mapMarkers = [];
$scope.clickEventsObject = {
mouseover: markerMouseOver,
mouseout: markerMouseOut
};
$scope.drawEventHandler = {
polygoncomplete: polygonComplete
};
function polygonComplete(drawingManager, eventName, scope, args) {
//$scope.drawingManagerControl.getDrawingManager().setDrawingMode(null); works here
//code...
});
[etc]..
And the other controller for the template with custom controls is:
app.controller('mapWidgetCtrl', ['$scope', function ($scope) {
$scope.toogleMap = function() {
console.log('Change map view via drawingManager');
};
}]);
I am having a difficutly into changing map view manually in both controllers (can't pass it somehow from one to another either). It seems I can't find a way to get the instance of drawManager outside of anything except polygonComplete function.
Any advice would be helpful, thank you.
You could introduce a service to share Drawing Manager Control across controllers:
app.service('sharedMapProperties', function () {
var drawingManagerControl = {};
return {
setDrawingManagerControl: function (value) {
drawingManagerControl = value;
},
getDrawingManagerControl: function () {
return drawingManagerControl;
}
}
});
Now you could save the control (drawingManagerControl variable) once the map is initialized:
app.controller('mapSearchCtrl', function ($scope, uiGmapIsReady, sharedMapProperties) {
//the remaining code is omitted..
$scope.drawingManagerControl = {};
sharedMapProperties.setDrawingManagerControl($scope.drawingManagerControl);
});
and then get control once the button clicked:
app.controller('mapWidgetCtrl', ['$scope', 'sharedMapProperties', function ($scope, sharedMapProperties) {
$scope.toogleMap = function () {
var control = sharedMapProperties.getDrawingManagerControl();
var drawingManager = control.getDrawingManager();
drawingManager.setDrawingMode(google.maps.drawing.OverlayType.CIRCLE);
console.log('Change map view via drawingManager');
};
}]);
Working example
So I'm trying to get use to angular and having some troubles trying to call a directive (google maps https://github.com/davidepedone/angular-google-places-map) and performing reverse geocoding. I think this would be a more general directives questions though.
I am trying to call a function within the directive to update the google maps place information as well as map. The way I'm thinking in my head is that I would need to pass a variable through the controller, scope that variable to the directive and then the directive will run the function?
UPDATED:
<div class="row">
<places-map selectedid="selectid(place.id)"></places-map>
</div>
<button ng-click="selectid(place.id)">{{place.id}}</button> </div>
With this click I suppose to go to the controller,
$scope.selectid= function (pickplaceid){
$scope.selectedid(pickplaceid);
}
Then the selectplaceid should be in the scope variables of the directive.
scope: {
customCallback: '&?',
picked: '=?',
address: '=?',
fallback: '=?',
mapType: '#?',
readonly: '#?',
responsive: '#?',
draggable: '#?',
toggleMapDraggable: '=?',
placeNotFound: '=?',
updateMarkerLabel: '=?',
selectedid:'='
},
and can call my method as so:
link: function ($scope, element, attrs, controller) {
//everything else from angular-google-places
$scope.selectedid= function (selectedplace)
{
///Whatever I want to do to geocode with the placeid
}
I think I may just be doing this completely wrong having really no luck with the directive call at all. I'm trying to update my map based on the location that I click and pull out the information of that specific place from the placeId. Any help would be great.
I have almost same thing working, and I solved it with a Service that receives a placeId (in my code it's called addressId, but it's the placeId Google Maps expects). In my service, I use the placeId to retrieve address details:
app.service('AddressDetailsService', ['$q', function ($q) {
this.placeService = new google.maps.places.PlacesService(document.getElementById('map'));
this.getDetails = function (addressId, address) {
var deferred = $q.defer();
var request = {
placeId: addressId
};
this.placeService.getDetails(request, function (place, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
address.placeId = addressId;
address.street = getAddressComponent(place, 'route', 'long');
address.countryCode = getAddressComponent(place, 'country', 'short');
address.countryName = getAddressComponent(place, 'country', 'long');
address.cityCode = getAddressComponent(place, 'locality', 'short');
address.cityName = getAddressComponent(place, 'locality', 'long');
address.postalCode = getAddressComponent(place, 'postal_code', 'short');
address.streetNumber = getAddressComponent(place, 'street_number', 'short');
address.latitude = place.geometry.location.lat();
address.longitude = place.geometry.location.lng();
if (address.streetNumber) {
address.streetNumber = parseInt(address.streetNumber);
}
deferred.resolve(address);
}
});
return deferred.promise;
};
function getAddressComponent(address, component, type) {
var country = null;
angular.forEach(address.address_components, function (addressComponent) {
if (addressComponent.types[0] === component) {
country = (type === 'short') ? addressComponent.short_name : addressComponent.long_name;
}
});
return country;
}
}]);
Then you inject it and call the service from your directive. This is the one I use, you might need to adapt it, but you see the idea. Instead of a link function, I use a controller for the directive:
.directive('mdAddressDetails', function mdAddressDetails() {
var directive = {
restrict: 'EA',
scope: {
address: '='
},
bindToController: true,
templateUrl: 'modules/address/addressDetails.html',
controller: AddressDetailsController,
controllerAs: 'dir'
};
AddressDetailsController.$inject = ['AddressDetailsService', '$q'];
function AddressDetailsController(AddressDetailsService, $q) {
var dir = this;
dir.selectAddress = selectAddress;
function selectAddress(address) {
if ((address) && (address.place_id)) {
AddressDetailsService.getDetails(address.place_id, dir.address).then(
function (addressDetails) {
dir.address = addressDetails;
}
);
}
}
}
return directive;
});
And then you just call the directive with the wanted parameter:
<md-address-details address="myAddress"></md-address-details>
I'm making a little app for AngularJS personal training, and i'm getting a few errors.
I receive Json data from an API, and then I want to display amarker for every object I get. The problem is that I have a console error, so I supposed I've made a mistake.
Here goes 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;
for (var i = 0; i < data.length; i++) {
$scope.markers.osloMarker = {
lat: data[i].position.lat,
lng: data[i].position.lng,
message: data[i].name,
focus: true,
draggable: false
};
$scope.osloCenter = {
lat: data[1].position.lat,
lng: data[1].position.lng,
zoom: 15
};
}
console.log(data.position.lat);
console.log(data.position.lng);
});
}]);
In my map I only have a marker for my last object. And in the console I have "TypeError: Cannot read property 'lat' of undefined"
When I try to display all my object outside of the map, in a list with ng-repeat, I have no problem.
Here go my HTML :
<div ng-controller="toulouseVeloListCtrl">
<leaflet markers="markers" center="osloCenter" style="width: 100%; height: 500px;"></leaflet>
</div>
Any idea of what is wrong ?
Thank you a lot !!
I guess that problem is:
console.log(data.position.lat);
Maybe you should use :
console.log(data[i].position.lat);
And also:
$scope.markers=[];
$scope.osloCenter=[];
for (var i = 0; i < data.length; i++) {
$scope.markers[i].osloMarker = {
lat: data[i].position.lat,
lng: data[i].position.lng,
message: data[i].name,
focus: true,
draggable: false
};
$scope.osloCenter[i] = {
lat: data[1].position.lat,
lng: data[1].position.lng,
zoom: 15
};
}
I am trying to write a directive for the jeditable plugin so when it changes the value, it will change also edit the model of the edited element.
So i wrote something like that, JS Fiddle
but i don`t know how to get the object that bound to the object in the list.
JS:
var app = angular.module("app", []);
app.controller('ctrl', function ($scope) {
$scope.lst = [{
id: 1,
name: "item1"
}, {
id: 1,
name: "item1"
}, {
id: 2,
name: "item2"
}, {
id: 3,
name: "item3"
}, {
id: 3,
name: "item3"
}];
});
app.directive('uiEditable', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.editable("/echo/json/", {
onblur: 'submit',
onsubmit: function (response, settings) {
//here i need to update the model
}
});
}
};
});
This uses ngModel to update back to the model. (so don't forget ng-model on element)
app.directive('uiEditable', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) return; // do nothing if no ng-model
element.editable(function (val) {
var tVal = $.trim(val);
if (ngModel.$viewValue !== tVal)
scope.$apply(function () { return ngModel.$setViewValue(tVal); });
return tVal;
});
}
};
});
Why are you using the jeditable plugin? This plugin seems to only duplicate in jQuery what you could already do in angular using ng-model alone and no plugin required.
If you just want to create text which can be edited in place like jEditable does, instead of creating a custom directive simply using ng-submit, ng-click, ng-hide and ng-model. Here's a rough example.
The view:
<form ng-submit="submit()">
<div ng-hide="showEdit"
ng-click="showEdit = true">
{{foo.bar}}
</div>
<div>
<input type="text"
ng-show="showEdit"
ng-model="foo.bar" />
</div>
<a href="#" ng-show="showEdit"
ng-click="submit();">done</a>
</form>
And the controller:
app.controller('myCtrl', function($scope) {
$scope.foo = {
bar: 'some text'
};
$scope.showEdit = false;
$scope.submit = function() {
// hide the edit field
$scope.showEdit = false;
// submit form
console.log('submit form');
}
});
Pass your item in in an isolated scope:
app.directive('uiEditable', function(){
return {
restrict: 'A',
scope: {
item: '='
},
link: function(scope, element, attrs){
element.editable("/echo/json/", {
onblur: 'submit',
onsubmit: function(response, settings){
alert(scope.item);
}
});
}
};
});
'scope.item' will now give you a reference to the item inside your directive.