Angular UI Map Not Updating for Scope Changes - angularjs

I am trying to update the Angular ui-map when the scope changes. I have added a $watch and I can see the scope change in console but the map is not updating with the change in latitude and longitude. This is what I have:
$scope.mapOptions = {
zoom: 4,
center: new google.maps.LatLng(-25.363882,131.044922),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var updateCenter = function() {
$scope.mapOptions = {
zoom: 11,
center: new google.maps.LatLng($scope.fromLat, $scope.fromLng),
};
console.log($scope.mapOptions);
}
$scope.$watch('fromLat', updateCenter);
$scope.$watch('fromLng', updateCenter);
Where the $scope.fromLat and $scope.fromLng are the scope changes that I am watching. When I log these scopes in console I can see the scopes updated. But the map is still on the initial state.

Okay, this is what I did to resolve this issue. I did 2 changes:
1) Used setCentre() in Controller:
In the updateCenter() method, instead of updating the $scope.mapOptions, I used setCenter() to directly change the map center.
var updateCenter = function() {
$scope.myMap.setCenter(new google.maps.LatLng($scope.fromLat, $scope.fromLng));
}
Here, myMap is the scope added to ui-map like this:
div ui-map="myMap" ui-options="mapOptions" class="google-map"></div>
2) ng-change in View or $watch:
Secondly, I added a $watch for updating the map instantly. There are 2 ways:
I can add ng-change attribute to the input scope that calls the above function when the latitude and longitudes scopes are changed. This way the map change is in effect immediately.
<input id="fromLat" type="text" ng-model="fromLat" ng-change="updateMap()" name="fromLat" />
Or add a $watch in controller:
$scope.$watch('fromLat', updateCenter);

Related

why to make directive in angularjs not renderning second time?

why my google directive not working ? Actually I make a simple directive of google map .And display on view.it work first time .But not work for second time .I will explain more When I run my plunker it show me Qutub minar google map.But when I click
‘+’ icon and press done button add another location example “Delhi” it give me longitude and latitute but not display the map
here is my code
Issue on this fuction I think
this.loadMap = function(latLng) {
console.log("function latlng called");
console.log(latLng);
google.maps.visualRefresh = true;
var myCenter = new google.maps.LatLng(latLng.latitude, latLng.longitude);
var map = new google.maps.Map(document.getElementById("id"), mapProp);
var marker = new google.maps.Marker({
position: myCenter,
});
marker.setMap(map);
}
You actually have two major troubles :
You generate multiple div with the same id (googleMap).
When you run the directive it get element by id but there is multiple elements with this ID which cause it to mess.
The main problem is that generating the google-map shouldn't be the responsibility of your "pane" directive but should be a totally new directive instead.
You can see it working in this plunker :
What i did :
I create a new directive myGoogleMap which basically use your old loadMap function (That i removed from the tabs directive). I also took the "latlng" attribute from the pane and put it on this directive.
.directive('myGoogleMap', function(){
return {
scope: {
latlng: '='
},
link: function(scope, element, attrs){
var latLng = scope.latlng;
console.log("function latlng called");
console.log(latLng);
google.maps.visualRefresh = true;
var myCenter;
myCenter=null;
myCenter = new google.maps.LatLng(latLng.latitude, latLng.longitude);
var mapProp = {
center: myCenter,
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(element[0], mapProp);
var marker = new google.maps.Marker({
position: myCenter,
});
marker.setMap(map);
}
}
The main difference is that the directive that generate the google map is already on the good element. I'll be able to access this element with element[0] in the link function. That mean i can actually generate a google map everywhere else giving a latlng object and using this directive.
Now your ng-repeat for pane looks like this :
<div ng-repeat="tabInfo in tabPlaces.tabItems" pane title="{{ tabInfo.title}}" ng-show="tabInfo.selected">
<p>{{ tabInfo.content }}</p>
<div my-google-map latlng="tabInfo.latlng" ng-style="mapConfig" style="height:200px;width:100%"></div>
</div>
Hope it helped.

$locationChangeSuccess triggers four times

I am new to angular Js.
My application flow is as below:
1) I have a view controller wherein, each view controller sets the breadcrumb data with the help of Breadcrumbs factory.
2) Breadcrumbs factory takes data from view controller and attaches data to $location.$$state object.(reason for storing in state object is if back button is pressed, view controller doesn't instantiate so I can refer history data for breadcrumbs ) below is code to attach data to state object:
var state = $location.state();
state.breadcrumb = breadcrumbData;
$location.replace().state(state);
3) I have also created breadcrumb directive on global header which will display breadcrumbs on $locationChangeSuccess event. Directive will take data from $location.state(); which was set in factory.
My problem is when location is changed, $locationChangeSuccess event callback function executes four times.
below is my directive code:
angular.module('cw-ui')
.directive('cwBreadcrumbs', function($location, Breadcrumbs, $rootScope) {
return {
restrict: 'E',
replace: true,
templateUrl: 'UI/Directives/breadcrumb',
link: function($scope, element){
//some code for element...
$rootScope.$on('$locationChangeSuccess', function(event, url, oldUrl, state, oldState){
// get data from history of location state
var data = $location.state();
console.log(data);
});
}
};
});
output is as below:
Object {}
Object {key: "Core/Views/dash:1", view: "Core/Views/dash", parameters: Array[0], breadcrumb: Array[2]}
Object {key: "Core/Views/dash:1", view: "Core/Views/dash", parameters: Array[0]}
Object {key: "Core/Views/dash:1", view: "Core/Views/dash", parameters: Array[0]}
breadcrumb: Array[2] disappears 1st, 3rd and 4th times. I really don't know what is causing this callback function execute four times, and I have no clue about an issue and don't know how to debug. Please help guys!
After running into this myself, the problem lies in the fact you are using the root scope to bind the locationChangeSuccess event from within a directive that is either encountered multiple times on a single page, or encountered multiple times as you revisit the page:
$rootScope.$on('$locationChangeSuccess', function(event, url, oldUrl, state, oldState){
Since you are binding to the rootScope, and the rootScope does not go out of scope, the event binding is not cleaned up for you.
Inside your link function, you should add a listener for the element $destroy, as well as capture the return value from the original bind, so you can later unbind it.
First: capture return value:
var unbindChangeSuccess = $rootScope.$on('$locationChangeSuccess' ...
Next, unbind that value in your destroy method:
element.on('$destroy', function() {
unbindChangeSuccess();
});
That should solve the multiple calls to your locationChangeSuccess! :)

angular leaflet custom markers (using angular directives)

I'm trying to create a "leaflet marker" using an angular directive. For design purposes, we separate the presentation and the model so different persons can work on different parts of the application. My issue, more likely, is more a "scope" problem than a "leaflet" problem. I'm trying to pass an object to be used in the angular directive, while I'm adding the markers on "$scope", in the controller. That directive, "" in my app, is the only tag in my "message" property on each marker object to be presented in the map. It has an attribute "estacao" which in portuguese is the same as "station".
So, my code is here:
angular.forEach($scope.estacoes, function(estacao) {
$scope.markers.push({
lat: estacao.latitude,
lng: estacao.longitude,
message: "<popup estacao='estacoes[" + i + "]'></popup>"
});
i++;
});
http://plnkr.co/edit/evaQpqGZUz39Y7MNqbo7?p=preview
The problem seams to be that my "estacao" is null when the directive is processed.
Can anyone help me to figure out what is happening?
The 'auto' compile of the popup message (from the leaflet directive) uses the root scope.
So you need to assign your response estacoes to the root scope:
promiseEstacoes.then(function(estacoes) {
$rootScope.estacoes = estacoes.estacoes;
...
}
http://plnkr.co/edit/OkQcth2zNrEdO2rgwBv8?p=preview
With the latest versions of angular-leaflet-directive, you can specify a scope to use on message rendering:
$scope.markers.push({
lat: estacao.latitude,
lng: estacao.longitude,
getMessageScope: function() { return $scope; }
message: "<popup estacao='estacoes[" + i + "]'></popup>"
});

AngularJS: 'google-maps': call directive in infoWindow

I'm using http://nlaplante.github.io/angular-google-maps/ to display a map in my angular Application.
I have a general controller for my page getting a Json.
to display markers, i'm using $watch in the scope cause I will do real time and the markers positions can change.
$scope.model = new Model 'api/now.json'
$scope.state = new DState
$scope.$watch ->
markers = []
_($scope.model.objects).each (obj) ->
markers.push
latitude: obj.latitude
longitude: obj.longitude
infoWindow: "<info-window>SHOULD NOT DISPLAY CAUSE DIRECTIVE</info-window>"
markers
, (newValue) ->
$scope.state.map.markers = newValue
, true
My directive is basic:
am.directive "infoWindow", ->
restrict: 'E'
template: "<div>IN DIRECTIVE</div>"
replace: true
My Html page calling the map:
#dashboard{ng:{controller: 'dashboardCtrl'}}
#map.google-map{center: 'state.map.center',
zoom: 'state.map.zoom',
markers: 'state.map.markers',
draggable: 'true'}
And The DState Factory to define the state:
.factory 'DashboardState', (Media) ->
class DashboardState
defaults:
map:
center:
latitude: 45.764043
longitude: 4.835659
zoom: 10
markers: []
selectedObj: null
constructor: (initialData) ->
_(#defaults).extend initialData
_(this).extend #defaults
So, my display here in my infoWindow is
SHOULD NOT DISPLAY CAUSE DIRECTIVE
But I should have what is in my directive:
IN DIRECTIVE
My directive is not called ... Do you have an idea?
It's a double question here, I would like to set the SelectedObj of my factory to the Obj himself. Do you have an Idea how to handle the event click on marker and where to place it to call the method who could assign my obj to SelectedObj?
Thanks by advance
You can try the following:
get the latest version of angularjs-ui-maps
Use the tempalteUrl property of the directive - you can add your directives inside the template (see how this is done in the example.html that's included in the source).
This way you can avoid any custom compilation of html
I'd faced a similar problem: https://stackoverflow.com/questions/20843463/angularjs-using-a-directive-in-google-maps-marker-window
In case you find any other approach, please do post it.
HTH

Sharing scope between controller & directive in AngularJS

I've created a directive to wrap a jQuery plugin, and I pass a config object for the plugin from the controller to the directive. (works)
In the config object is a callback that I want to call on an event. (works)
In the callback, I want to modify a property on the controller's $scope, which does not work. Angular does not recognize that the property has changed for some reason, which leads me to believe that the $scope in the callback is different than the controller's $scope. My problem is I just don't why.
Can anybody point me in the right direction?
Click here for Fiddle
app.js
var app = angular.module('app', [])
.directive('datepicker', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
// Uncommenting the line below causes
// the "date changed!" text to appear,
// as I expect it would.
// scope.dateChanged = true;
var dateInput = angular.element('.datepicker')
dateInput.datepicker(scope.datepickerOpts);
// The datepicker fires a changeDate event
// when a date is chosen. I want to execute the
// callback defined in a controller.
// ---
// PROBLEM:
// Angular does not recognize that $scope.dateChanged
// is changed in the callback. The view does not update.
dateInput.bind('changeDate', scope.onDateChange);
}
};
});
var myModule = angular.module('myModule', ['app'])
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.dateChanged = false;
$scope.datepickerOpts = {
autoclose: true,
format: 'mm-dd-yyyy'
};
$scope.onDateChange = function () {
alert('onDateChange called!');
// ------------------
// PROBLEM AREA:
// This doesnt cause the "date changed!" text to show.
// ------------------
$scope.dateChanged = true;
setTimeout(function () {
$scope.dateChanged = false;
}, 5000);
};
}]);
html
<div ng-controller="MyCtrl">
<p ng-show="dateChanged">date changed!</p>
<input type="text" value="02-16-2012" class="datepicker" datepicker="">
</div>
There are a number of scope issues at work in your demo. First , within the dateChange callback, even though the function itself is declared inside the controller, the context of this within the callback is the bootstrap element since it is within a bootstrap handler.
Whenever you change angular scope values from within third party code , angular needs to know about it by using $apply. Generally best to keep all third party scopes inside the directive.
A more angular apprroach is to use ng-model on the input. Then use $.watch for changes to the model. This helps keep all the code inside the controller within angular context. Is rare in any angular application not to use ng-model on any form controls
<input type="text" class="datepicker" datepicker="" ng-model="myDate">
Within directive:
dateInput.bind('changeDate',function(){
scope.$apply(function(){
scope[attrs.ngModel] = element.val()
});
});
Then in Controller:
$scope.$watch('myDate',function(oldVal,newVal){
if(oldVal !=newVal){
/* since this code is in angular context will work for the hide/show now*/
$scope.dateChanged=true;
$timeout(function(){
$scope.dateChanged=false;
},5000);
}
});
Demo: http://jsfiddle.net/qxjck/10/
EDIT One more item that should change is remove var dateInput = angular.element('.datepicker') if you want to use this directive on more than one element in page. It is redundant being used in directive where element is one of the arguments in the link callback already, and is instance specific. Replace dateInput with element
The changeDate event bound to the input seems to be set up to fire outside of the Angular framework. To show the paragraph, call $scope.$apply() after setting dateChanged to true. To hide the paragraph after the delay, you can use $apply() again inside the function passed to setTimeout, but you're likely to keep out of further trouble using Angular's $timeout() instead.
Fiddle

Resources