AngularJS marker coordinates are not being updated in model - angularjs

I' was trying out angular map and I faced a problem which was related to getting map marker position and display it when marker is moved. But marker model was not updated when marker was moved. I used the official example and solve the problem by changing the dragend event. I used the apply to change the marker coordinates value
events: {
dragend: function (marker, eventName, args) {
$log.log('marker dragend');
$log.log(marker.getPosition().lat());
$log.log(marker.getPosition().lng());
$scope.$apply(function(){
$scope.marker.coords.latitude = marker.getPosition().lat();
$scope.marker.coords.longitude = marker.getPosition().lng();
});
}
}
The complete example can be seen here.
But I'm not satisfied with this approach. Is there any solution where I do not need to put in this block to update marker coords
$scope.$apply(function(){
$scope.marker.coords.latitude = marker.getPosition().lat();
$scope.marker.coords.longitude = marker.getPosition().lng();
});

There are two ways to send updated data to your directive.
$apply: If you want to update data which is coming from parent controller and other directives are also using the same then use this. However, after this function $digest will call to update local variables.
$digest: If you want to update directive level scope variables then you can directly call this method.

Related

angular-google-maps - listen for infowindow domready event

Listening for the domready event in normal js Google maps is relatively easy
as outlined here :
infoWindow = new google.maps.InfoWindow();
google.maps.event.addListener(infoWindow, 'domready', function() {
// whatever you want to do once the DOM is ready
});
However it doesn't seem obvious how to do it in angular-google-maps.
Is there a solution ?
The solution when using Angular Google Maps involves using the infowindow control object - see docs.
As noted in this issue - where I first posted this solution - you can create an empty infowindow control object within the main controller :
$scope.infowindowControl = {};
Then you scope bind your new object in the ui-gmap-window directive definition like :
<ui-gmap-window
options="windowOptions"
closeClick="closeClick()"
control="infowindowControl"
>
On infowindow open (actually unsure at what point) - it will pass five functions to the empty object you created within $scope. Some of these functions are noted in the documentation but in a rather haphazard and non-defined way :
getChildWindows()
getGWindows()
getPlurals()
hideWindow()
showWindow()
The one that you need is getGWindows() - which you put inside your marker click event.
This will get you an array of the open infowindows and you can now attach an event listener in the standard google maps fashion, like :
var windows = $scope.infowindowControl.getGWindows();
console.log('inside click after getGWindows ', windows);
google.maps.event.addListener(windows[0], 'domready', function() {
console.log('infowindow domready just fired !!');
});
While it's not an ideal, well documented or easy solution (and took me a number of hours to figure out) - and passing functions to an empty object is frankly counter-intuitive - it does seem to work.
Plunker here.

Watch for dom element in AngularJS

I'm looking for a pure angularJS way to call a controller method once a particular dom element is rendered. I'm implementing the scenario of a back button tap, so I need to scroll to a particular element once it is rendered. I'm using http://mobileangularui.com/docs/#scrollable.
Update: how my controller looks like:
$scope.item_ready=function(){
return document.getElementById($scope.item_dom_id).length;
};
$scope.$watch('item_ready', function(new_value, old_value, scope){
//run once on page load, and angular.element() is empty as the element is not yet rendered
});
Thanks
One hack that you could do and I emphasize hack here but sometimes it's just what you need is watch the DOM for changes and execute a function when the DOM hasn't changed for 500ms which is accepted as a fair value to say that the DOM has loaded. A code for this would look like the following:
// HACK: run this when the dom hasn't changed for 500ms logic
var broadcast = function () {};
if (document.addEventListener) {
document.addEventListener("DOMSubtreeModified", function (e) {
//If less than 500 milliseconds have passed, the previous broadcast will be cleared.
clearTimeout(broadcast)
broadcast = $window.setTimeout(function () {
//This will only fire after 500 ms have passed with no changes
// run your code here
}, 10)
});
}
Read this post Calling a function when ng-repeat has finished
But don't look at the accepted answer, use the 3rd answer down by #Josep by using a filter to iterate through all your repeat items and call the function once the $last property returns true.
However instead of using $emit, run your function...This way you don't have to rely on $watch. Have used it and works like a charm...

Angular.js view rendering (with a bit more than just showing the retrieved values)

I'm working on a view that allows user to type in the postcode and then it will give the location info about the postcode (lat, long, city name, etc), as well as marking the location on a map (using google map api).
For the postcod part, it is rather straight forward, I placed a few {{}} directives in the view and assign the corresponding values to the $scope in the controller. But when it comes to marking that location on the google map view, it gets a bit confusing. here is the sample code to mark a place in google map:
var containerID = 'map-container'; // id of a DIV
var lat = 50.2;
var lng = -3.6;
var container = document.getElementById(containerID);
var options = {
zoom: 15,
center: new google.maps.LatLng(lat, lng),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(container, options);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map: map,
title: markerTitle
});
As you can see, the code itself only involves rendering the map, and to me it should sit inside the view because a view is supposed to take the returned result from the controller and do whatever magic it wants to present that result. And since the code involves manipulating the DOM, which is another reason I don't think it should be in the controller.
But am I correct to make this assumption?
And even if i'm correct, how could I implement this?
You are right, DOM Manipulation should not be in the controller, but it shouldn't be in the view neither.
The Angular way would be to create a directive that handle all DOM Manipulation. This directive is independent and work alone, and is here to do the dirty job. Once you've created it, you add it in the view, and pass it the parameters from the controllers.
So the view display the element, the controllers set the model and values, and the directive handle DOM manipulation/graphic change,value processing ...etc.
Good news for you, I think that in your case the directive has already been made, you may want to take a look at : AngularUI GoogleMap Directive
Try writing your asynchronous codes inside.
$scope.$apply(function{
})

Google maps not always fully rendering in Ionic

Having trouble with always rendering google maps in my Ionic app. When I first land on a view from a list of items on the previous view, the map always renders in its complete state. However, if I go back to the previous view and tap a different business, or even the same one, it appears as if the map is only rendering 25% of the complete map. I'm having this issue on both the emulator and on my iPhone.
Example
Code
getData.getBusinesses()
.then(function(data) {
// get businesses data from getData factory
})
.then(function(data) {
// get businesses photo from getData factory
})
.then(function(data) {
// get some other business stuff
})
.then(function() {
// get reviews for current business from separate async call in reviews factory
})
.then(function() {
// instantiate our map
var map = new GoogleMap($scope.business.name, $scope.business.addr1, $scope.business.city, $scope.business.state, $scope.business.zip, $scope.business.lat, $scope.business.long);
map.initialize();
})
.then(function() {
// okay, hide loading icon and show view now
},
function(err) {
// log an error if something goes wrong
});
What doesn't make sense to me is that I'm using this exact code for a website equivalent of the app, yet the maps fully load in the browser every time. The maps also fully load when I do an ionic serve and test the app in Chrome. I did also try returning the map and initializing it in a following promise, but to no avail.
I've also tried using angular google maps, but the same issue is occurring. I think I might want to refactor my gmaps.js (where I'm creating the Google Maps function) into a directive, but I don't know if that will actually fix anything (seeing as angular google maps had the same rendering issue).
I don't think the full code is necessary, but if you need to see more let me know.
EDIT
It seems that wrapping my map call in a setTimeout for 100ms always renders the map now. So I guess the new question is, what's the angular way of doing this?
I'm seeing similar issues with ng-map in Ionic. I have a map inside of a tab view and upon switching tabs away from the map view and back again, I would often see the poorly rendered and greyed out map as you describe above. Two things that I did that may help fix your issue:
Try using $state.go('yourStateHere', {}, {reload: true}); to get back to your view. The reload: true seemed to help re-render the map properly when the map was within the tab's template.
After wrapping the map in my own directive, I found the same thing happening again and wasn't able to fix it with the first suggestion. To fix it this time, I started with #Fernando's suggestion and added his suggested $ionicView.enter event to my directive's controller. When that didn't work, I instead added a simple ng-if="vm.displayMap" directive to the <ng-map> directive and added the following code to add it to the DOM on controller activation and remove it from the DOM right before leaving the view.
function controller($scope) {
vm.displayMap = true;
$scope.$on('$ionicView.beforeLeave', function(){
vm.displayMap = false;
});
}
Hope that helps.
don't use setTimeout on this!
You need to understand that the map is conflicting with the container size or something (example: map is loading while ionic animation is running, like swiping).
Once you understand this, you need to set map after view is completely rendered.
Try this on your controller:
$scope.$on('$ionicView.enter', function(){
var map = new GoogleMap($scope.business.name,
$scope.business.addr1, $scope.business.city,
$scope.business.state, $scope.business.zip,
$scope.business.lat, $scope.business.long);
map.initialize();
});

Replacing object in array - is it safe for Angular bindings

I'm using Angular JS and I've implemented my form with a reset function.
setPrimarySelectionEditEntry is called when I start editing the item on the form. primarySelectionEntry keeps the object that is bound to controls
savePrimarySelectionEdit is called when user hits Save. What I do is I replace item in primarySelection array, as I use this array to display a list
cancelPrimarySelectionEdit does nothing except setting the form to pristine state. Underlying array remains intact
$scope.cancelPrimarySelectionEdit = function () {
$scope.primarySelectionForm.$setPristine();
};
$scope.savePrimarySelectionEdit = function () {
$scope.topicSelection.primarySelection[$scope.formEntryIndex] = $scope.primarySelectionEntry;
$scope.cancelPrimarySelectionEdit();
};
$scope.setPrimarySelectionEditEntry = function (entry) {
$scope.formEntryIndex = $scope.topicSelection.primarySelection.indexOf(entry); ;
$scope.primarySelectionEntry = angular.copy(entry);
};
It works: when I hit save item in the list reflects new values. When I hit cancel, then nothing happens. But I wonder if this replacing array item is safe in the context of angular?
As long as you change the model within Angular's execution context it'll be safe to replace some elements or even the whole array. It won't break neither the data binding nor the event loop.
You only have to worry about changes done outside Angular's loop. For example, a DOM event listener in a directive. Let's say you have the following directive:
angular.module('myApp').directive('resetForm', function(){
return {
link: function($scope, $iElement){
//Add a listener to the element
$iElement.on('click', function(){
//Carefull: changes in scope in this context won't trigger an angular event loop.
$scope.cancelPrimarySelectionEdit();
//So, call $digest to process all the watchers
$scope.$digest();
return;
});
return;
}
}
});
Here, the $scope.cancelPrimarySelectionEdit() function can be called at any time, so a call to $scope.$apply() or $scope.$digest() should be added to tell Angular that something in the model has changed and needs to execute all the watchers from the current scope and its children.

Resources