Angular-ui ui-map click event not receiving $params - angularjs

I'm trying to implement Google maps in Angularjs using ui.Map (http://angular-ui.github.io/ui-map/)
I've followed the example pretty closely and the map loads, I can create a marker in the map center and the 'map-tilesloaded' event works fine.
My problem is adding a marker where the user clicks. The click function is receiving an empty $params parameter. In my controller:
$scope.newMapOptions = {
center : new google.maps.LatLng($scope.position.lat, $scope.position.lng),
zoom : 18,
mapTypeId : google.maps.MapTypeId.ROADMAP
};
$scope.getLocation = function() {
if (navigator.geolocation) {
return navigator.geolocation.getCurrentPosition(setPosition);
}
};
$scope.addMarker = function($event, $params) {
$scope.newTingMarker = new google.maps.Marker({
map : $scope.myNewTingMap,
position : $params[0].latLng
});
};
$scope.initMap = function() {
if (!$scope.mapLoaded)
$scope.getLocation();
$scope.mapLoaded = true;
};
function setPosition(pos) {
$scope.position = {
lat : pos.coords.latitude,
lng : pos.coords.longitude
};
$scope.meMarker = new google.maps.Marker({
map : $scope.myNewTingMap,
position : new google.maps.LatLng($scope.position.lat, $scope.position.lng)
});
$scope.myNewTingMap.setCenter(new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude));
$scope.$apply();
}
The html:
<div ui-map-info-window="myInfoWindow">
<b>Current location</b>
</div>
<div ui-map-marker="meMarker" ></div>
<div ui-map-marker="newTingMarker" ui-event="{'map-click': 'openMarkerInfo(newTingMarker)'}"></div>
<section id="newTingMap" >
<div ui-map="myNewTingMap" ui-options="newMapOptions" class="map-canvas"
ui-event="{'map-tilesloaded': 'initMap()', 'map-click': 'addMarker($event, $params)' }"></div>
</section>
$scope.addMarker should receive $event and $params where $params[0] has the latlng object. At the moment is it an empty array: []
I'm using angular 1.1.5, but I've tried using the same as the ui.Map example with no effect.
I should also note that this is in a view but putting it outside the view in the main controller makes no difference.
If I try to follow the code running from the ui-map directive I can see that the latlng object does start off in the event:
ui-map.js:
angular.forEach(eventsStr.split(' '), function (eventName) {
//Prefix all googlemap events with 'map-', so eg 'click'
//for the googlemap doesn't interfere with a normal 'click' event
google.maps.event.addListener(googleObject, eventName, function (event) {
element.triggerHandler('map-' + eventName, event);
//We create an $apply if it isn't happening. we need better support for this
//We don't want to use timeout because tons of these events fire at once,
//and we only need one $apply
if (!scope.$$phase){ scope.$apply();}
});
});
element.triggerHandler('map-' + eventName, event); ... has the latlng object in 'event' but is seems to get lost after that

Not sure what your issue is, I took your code and created a fiddle that works fine(something you should have done).
I did a console log when you click that logs the $params.
The most important thing to note is your code crashes at first because you reference $scope.position.lat before setting it. I updated it to default to RVA.
,
You do need to handle the case a little more gracefully.
function MapCtrl($scope, watchArray) {
var center;
if ($scope.position) {
center = new google.maps.LatLng($scope.position.lat, $scope.position.lng);
}
else {
center = new google.maps.LatLng(37.5410, 77.4329); //if null use rva
}
$scope.newMapOptions = {
center: center,
zoom: 18,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
...
}
Console.log:
[Ps]
v 0: Ps
> la: Q
> latLng: O
> pixel: Q
> __proto__: Ps
length: 1
> __proto__: Array[0]

Related

ng-click in a loop is always clicked - ionic 2

I have a google map in my view and in this map I have many marker and each marker should have a content which contains an a href for more details
This is my code for adding one marker which is used in a loop on all the events saved in the database :
setMarker(event){
let coords = event.location.split(",");
let location = new google.maps.LatLng(coords[0],coords[1]);
let marker = new google.maps.Marker({
map: this.map,
position: location
});
let content = "<h1>" + event.name + "</h1></br><a ng-click="+this.viewEventDetails(event)+"> More Details</a>";
let infoWindow = new google.maps.InfoWindow({
content: content
});
google.maps.event.addListener(marker, 'click', () => {
infoWindow.open(this.map, marker);
});
return marker;
}
and this is my viewEventDetails function :
viewEventDetails(event){
this.navCtrl.push(EventDetailsPage, event);
}
When I load the map view it loads the viewEventDetails on all events , what should I do please ?
Could you change "</h1></br><a ng-click="+this.viewEventDetails(event)+"> More Details</a>" to "</h1></br><a ng-click=\"viewEventDetails(event)\"> More Details</a>". I think this should work.
Btw I think ng-click should also be substitude with (click) since ionic 2 does the angular2 way and therefore does not accept ng-click.

passing data from service to a controller in angular

I am having a small issue sending data from service to a controller with google API. can anyone have a look at code below and give me some advice?
injection is good and I don't see any errors. I tried few things.
1. normal binding using a service method(e.g. getCurrentPos()). it will return an object that stores the pos info
2. $rootScope.$broadcast
3. angular.copy()
//in the service--------
var position = new google.maps.LatLng(markers[i][1], markers[i][2]);
this.marker = new google.maps.Marker({
position: position,
map: map,
title: markers[i][0],
draggable: true,
icon: '../img/png/shopper1.png'
});
var personMarker = this.marker;
this.marker.addListener('drag', function() {
console.log('lat:'+personMarker.getPosition().lat()+' lng:'+personMarker.getPosition().lng());
currentPos.lat = personMarker.getPosition().lat();
currentPos.lng = personMarker.getPosition().lng();
// myPos = personMarker.getPosition();
myPos = [personMarker.getPosition().lat(), personMarker.getPosition().lng()];
angular.copy(myPos, scope.currentPos); //not working
console.log('scope.currentPos',scope.currentPos);
// $rootScope.$broadcast('evtUpdateMyPos', { //tried but not working.
// 'lat': personMarker.getPosition().lat(),
// 'lng': personMarker.getPosition().lng()
// });
console.log("mypos:", myPos);
});
//in the controller-------
$scope.currentPos = [];
//Listen on a broadcast event
// $scope.$on('evtUpdateMyPos', function (event, myPos){
// console.log('evtUpdateMyPos is fired.', myPos); //this logs here.
// // $scope.currentPos = angular.copy(myPos); //this dos not help
// // $scope.currentPos = myPos; //this does not hlep.
// })
In some cases when you're trying to update your model from an external library like in your case using google maps you should wrap the setter in something like this:
$timeout(function(){
myModel = 'Google.map.data'
});
or
$rootScope.$applyAsync(function(){ // this could also be $scope
myModel = 'Google.map.data'
});
myModel could be a property of your service or a $scope variable ($scope.myModel)

angular leaflet marker change property with function

I would like to change some leaflet marker properties by clicking a link outside the map, but it doesn't work.
Check out this fiddle:
http://jsfiddle.net/Ls59qLLa/2/
js:
var app = angular.module('demoapp',['leaflet-directive']);
app.controller('DemoController', [ '$scope', 'leafletData', function($scope, leafletData) {
var local_icons = {
defaultIcon: {},
gmapicon: {
iconUrl: 'http://maps.google.com/mapfiles/kml/shapes/capital_big.png',
iconSize: [25, 25],
iconAnchor: [12, 12],
popupAnchor: [0, 0]
}
}
angular.extend($scope, {
markers: {
m1: {
lat: 41.85,
lng: -87.65,
clickable: false,
message: "I'm a static marker",
icon: local_icons.gmapicon
}
}
});
$scope.makeIconClickable = function(){
alert('function called');
var whichmarker = 'm1';
$scope.markers[whichmarker].clickable = true;
}
}]);
HTML:
<body ng-controller="DemoController">
<leaflet markers="markers"></leaflet>
<a href="#" ng-click=makeIconClickable()>Make Icon Clickable</a>
</body>
Leaflet directive has separate $watch for every marker (and path) on the map. When you change one of the marker property, this $watch is fired and it checks if some of properties has changed. Apparently it does not look for the clickable property, but it looks for message property. So in your function you could set message of the marker and everything binds properly:
$scope.makeIconClickable = function(){
var whichmarker = 'm1';
$scope.markers[whichmarker].clickable = true;
$scope.markers[whichmarker].message = "I'm a static marker";
}
If you need to set message on marker initialization phase, you can use some temporary property:
angular.extend($scope, {
markers: {
m1: {
lat: 41.85,
lng: -87.65,
icon: local_icons.gmapicon,
tmpmessage: 'I am a static marker'
}
}
});
And then:
$scope.makeIconClickable = function(){
var whichmarker = 'm1';
$scope.markers[whichmarker].clickable = true;
$scope.markers[whichmarker].message = $scope.markers[whichmarker].tmpmessage;
}
Working example: http://jsfiddle.net/3zgL8m4u/
I had the same problem. I sometimes need the map to fire an onClick Event even if the user clicked the Marker. The answer provided by "Agnieszka ƚwiecznik" is a quick and easy workaround if all you need is no Popup to be shown. It does not solve my problem, since it only removes the Popup, but no onClick Event on the map is fired.
To solve this i found this official Thread which seems to have been opened by the OP: https://github.com/tombatossals/angular-leaflet-directive/issues/676
Here a link to a modified JSFiddle is Provided: http://jsfiddle.net/nmccready/gbd1aydL/
The user who provided this fix included the following codeblock in the "angular-leaflet-directive.js" which can be found in the jsfiddle under "external resources":
var _destroy = function(markerModels, oldMarkerModels, lMarkers, map, layers){
// Delete markers from the array
var hasLogged = false,
modelIsDiff = false;
var doCheckOldModel = isDefined(oldMarkerModels);
for (var name in lMarkers) {
if(!hasLogged) {
$log.debug(errorHeader + "[markers] destroy: ");
hasLogged = true;
}
if(doCheckOldModel){
//might want to make the option (in watch options) to disable deep checking
//ie the options to only check !== (reference check) instead of angular.equals (slow)
modelIsDiff = !angular.equals(markerModels[name],oldMarkerModels[name]);
}
if (!isDefined(markerModels) ||
!Object.keys(markerModels).length ||
!isDefined(markerModels[name]) ||
!Object.keys(markerModels[name]).length ||
modelIsDiff) {
deleteMarker(lMarkers[name], map, layers);
delete lMarkers[name];
}
}
};
Note that this seems to be an older Version, as noted in the file itself: "angular-leaflet-directive 0.7.11 2015-04-08". When included in my AngularJS Project this allowed me to change the clickable property of the marker by just modifiyng the corresponding boolean in my "$scope.markers"-equivalent.

TextAngular fileDropHandler documentation

We have just upgraded our textangular to 1.2.2, as this now supports drag and drop.
have seen defaultFileDropHandler within the textAngualrSetup, how ever, struggling to find any documentation to support this or how to use it.
defaultFileDropHandler:
/* istanbul ignore next: untestable image processing */
function (file, insertAction)
{
debugger;
var reader = new FileReader();
if(file.type.substring(0, 5) === 'image'){
reader.onload = function() {
if(reader.result !== '') insertAction('insertImage', reader.result, true);
};
reader.readAsDataURL(file);
return true;
}
return false;
}
Basically, we want to allow users to drag multiple pdf's, word docs etc and to upload on submit.
We could prob get this working in a fashion adding in functionality into defaultFileDropHandler within the settings page,
we implement ta by :-
<div text-angular data-ng-model="NoteText" ></div>
however, is there a cleaner way to achieve this?
Sorry about the lack of docs!
Basically the defaultFileDropHandler is triggered when the HTML element.on("drop") event is fired.
Implementing this via the textAngularSetup file is fine, but will be globally applied to all instances. To apply a handler for just one instance of textAngular use the ta-file-drop attribute which should be the name of a function on the scope with the same signature as defaultFileDropHandler. For Example:
JS In Controller
$scope.dropHandler = function(file, insertAction){...};
HTML
<div text-angular data-ng-model="NoteText" ta-file-drop="dropHandler"></div>
Both great answer, thank you!
I would just like to put the full code out to cover the global case since the code was only a snippet...
app.config( function( $provide ) {
$provide.decorator( 'taOptions', [ '$delegate', function( taOptions ) {
taOptions.defaultFileDropHandler = function( file, insertAction ) {
// validation
if( file.type.substring( 0, 5 ) !== "image" ) {
// add your own code here
alert( "only images can be added" );
return;
}
if( file.size > 500000 ) {
// add your own code here
alert( "file size cannot exceed 0.5MB" );
return;
}
// create a base64 string
var reader = new FileReader();
reader.onload = function() {
reader.result && insertAction( "insertImage", reader.result, true );
};
reader.readAsDataURL(file);
return true;
};
return taOptions;
}]);
});

Parsing The Response From Fetch Backbone

I am not able to get the response string from the fetch method in my view render method.
The Collection Class Goes here.
Collection = (function(){
var Events;
Events = Backbone.Collection.extend({
url:function(){
//alert(urlOptions);
return this.urlParam;
// alert('API call :'+urlOptions);
},
initialize: function(models, options){
this.urlParam = options.urlParam || "";
},
parse: function( response ){
var parsed = Jath.parse(
[ '//notes', {
} ], response );
console.log(parsed);
return parsed;
}
});
return {
newInstance : function(models,options) { return new Events(models,options); }
};
})();
The View Goes Here
View = (function() {
'use strict';
var
htmlTemplate = _.template( $('#eventGridTemplate' ).html() ), // See templatesSearch.jsp
expanded = true, // By default the Events Grid extends to the bottom of the browser window.
BackboneView, applyStyles;
/**
* Apply CSS specific to this view
* Unfortunately, this View needs to modify its parent wrapper element.
* Otherwise the layout will break when it's resized. See templatesSearch.jsp.
* #param {Object} $elmt
* #param {Boolean} expand
*/
applyStyles = function( $elmt, expand ) {
var
top = '2px',
left = '2px',
pos = 'absolute',
right = '2px';
if ( expand ) {
$elmt.css({
"position" : pos,
"top" : top,
"left" : left,
"right" : right,
"bottom" : "2px"
});
$elmt.parent().css( 'bottom', '2px' );
} else {
$elmt.css({
"position" : pos,
"top" : top,
"left" : left,
"right" : right,
"bottom" : "50%"
});
$elmt.parent().css( 'bottom', '50%' );
}
};
// See 'http://backbonejs.org/#View-constructor' for more info
BackboneView = Backbone.View.extend({
onAiringsBtn : function( event ) {
// If the Events Grid container was expanded, contract it.
// If it was contracted, expand it.
expanded = expanded ? false : true;
applyStyles( this.$('div'), expanded );
},
initialize : function() {
this.render();
},
render : function() {
// this.$el is the jQuery version of this.el
// Which is populated by options.el
// Which is part of the options object passed into the constructor
//alert('Start Date:' +$('#datepicker').datepicker('getDate'));
var eventsCollection = Collection.newInstance([],{urlParam:'http://localhost:8080/adtglobal/2.0/api/events?startDate=2013-11-05T00:00:00-0400&endDate=2013-11-06T00:00:00-0400'});
//console.log(eventsCollection.url());
eventsCollection.fetch({
success : function(eventsCollection , response){
console.log(eventsCollection.toJSON());
alert(eventsCollection.toJSON());
}
});
this.$el.html( htmlTemplate );
applyStyles( this.$('div'), true );
}
});
//-------------------------------------------------------------------------------------------------------------------
// Public API
//-------------------------------------------------------------------------------------------------------------------
return {
newInstance : function(options) { return new BackboneView(options); }
};
})();
I get The response as success and i see the xml in the browser console, but how do i parse it here ??
The response is here
<Events xmlns="urn:api:2.0">
<PageNavigation showing="45"></PageNavigation>
<Event id="400515625" href="400515625" source="SDR">
<OrgHierarchy>
<level id="56" typeId="100" title="Soccer" description="Sport" href="../sporthierarchy?levelId=56&levelTypeId=100"></level>
<level id="2902" typeId="101" title="UEFA" description="Confederation" href="../sporthierarchy?levelId=2902&levelTypeId=101" parentId="56" parentType="100"></level>
</OrgHierarchy>
<EventType id="1">Standard Event</EventType>
<League id="1860">UEFA > Polish Orange Ekstraklasa</League>
<EventTitleText>Ruch Chorzow vs. Zawisa Bydgoszcz</EventTitleText>
<CompetitionType id="1">Team vs Team</CompetitionType>
<EventCompetitors>
<Teams>
<HomeTeam id="73960" href="../teams/73960">Ruch Chorzow</HomeTeam>
<AwayTeam id="107278" href="../teams/107278">Zawisa Bydgoszcz</AwayTeam>
</Teams>
</EventCompetitors>
</Event>
</Events>
well, it seems you are not using Jath correctly.
you need to define a template that matches your XML.
I have not used Jath before, but I will try to create a simple template for you:
//in your parse
var template = [ "//Events", {id: "#id", eventType: 'EventType'}];
return Jath.parse(template, response );
try this first and see if you have something in your collection.
it seems you have nested resources in your response, so you will have to complete the template.
or, if you don't have to use Jath, try something simpler: here's an example jsfiddle, you should be able to see the parsed JSON in the console.
http://jsfiddle.net/5j57T/
EDIT: xml to json code is from: http://davidwalsh.name/convert-xml-json
NOTE: you need to parse that json again into an array of objects you need, in order to make your app work!
=====================
EDIT: another example (works better than the previous fiddle)
http://jsfiddle.net/5j57T/1/
uses a jquery library http://www.fyneworks.com/jquery/xml-to-json/
there's only one issue, since there's only one event in your events, it cannot be parsed to an array by default, you will have to do that manually. If, there's always only one event per response, you can do something like this:
parsed = [$.xml2json(xmlDoc.getElementsByTagName('event')[0])]
or you can just do
parsed = $.map(xmlDoc.getElementsByTagName('event'), function(eventXml) {
return $.xml2json(eventXml);
});
working jsfidde: http://jsfiddle.net/5j57T/2/
==========================
EDIT:
I think by default, backbone is expecting a JSON response from the server. You will have to overwrite it's fetch() function so support XML. so in your collection definition, add this:
fetch: function (options) {
options = options || {};
options.dataType = "xml";
return Backbone.Collection.prototype.fetch.call(this, options);
}
here's a working jsfiddle: http://jsfiddle.net/5j57T/4/
Ignore the 'data' and 'type' options I passed to fetch(), and the url, all of that are just to make Ajax request work on jsfiddle. You shouldn't have those in your app.
In this demo, when the XML is returned, it's parsed into the collection correctly. You should be able to see your collection in the console.
======
If all of your backend services return XML only. I'd suggest overwrite the Backbone.sync function to make it work for other methods like save().

Resources