Cannot set property 'opacity' of undefined in gmap-window - javascript

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

How do you show a window when clicking polygons in angular google maps?

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; }

Angular Maps DrawManager instance in Custom Control

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

Calling methods from directive

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>

$http angularJS loop for each object with leaflet issue

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
};
}

angularJS + jQuery jeditable plugin work together

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.

Categories