why to make directive in angularjs not renderning second time? - angularjs

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.

Related

Leaflet issues angular woes in showing and hiding? Want to get rid of $timeout

I have a leaflet being created with L.map('mapelement') being called. The issue is that if I click a button that "hides" the leaflet map, then click the button again to show, the leaflet map does not show up. However, when I put in a setTimeout within the link function before the map gets created and set it to 2 seconds, then the map shows every time (though I have to wait 2 seconds). Is there a better alternative to using $timeout in my custom "leaflet-map" directive to show and hide?
I created a naive example of a leaflet-map directive without seeing any of your code and am toggling the display of the map through ng-show. It works without any $timeout. It's hard to diagnose where your problems are stemming from without seeing any code or knowing how you are trying to toggle the map's display.
angular.module('demo', [])
.directive('leafletMap', function() {
return {
restrict: 'E',
scope: {
mapOptions: '&'
},
template: '<div><button ng-click="toggleShow()">Toggle Map</button><div class="demo-map" ng-show="isShown"></div></div>',
link: function(scope, elem, attrs) {
// Find element to bind to map
var mapElem = elem.children().find('div')[0],
// get map options from isolate scope
mapOptions = scope.mapOptions();
// State of hide/show
scope.isShown = true;
// Create Map.
var map = L.map(mapElem, mapOptions);
// Just taken from leaflet example
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpandmbXliNDBjZWd2M2x6bDk3c2ZtOTkifQ._QA7i5Mpkd_m30IGElHziw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(map);
// method to toggle the shown/hidden state of map
scope.toggleShow = function() {
scope.isShown = !scope.isShown;
};
// cleanup on element destroy
elem.on('$destroy', function() {
map.remove();
});
}
};
})
.controller('DemoController', function() {
this.mapOptions = {
center: [51.505, -0.09],
zoom: 13
};
});
.demo-map {
height: 500px;
}
<script src="//cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link rel="stylesheet" href="//cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
<div ng-app="demo" ng-controller="DemoController as ctrl">
<leaflet-map map-options="ctrl.mapOptions"></leaflet-map>
</div>
Would CSS help you ?
Create one map in a visible div
visibility: visible
Create the second map in a hidden div
visibility: hidden
Position both your div in the same position
position: absolute
When you want to toggle just change the visibility of your divs
Example: http://plnkr.co/edit/voTjyMLKTxUC183nf8ML?p=preview
(Sorry it's not angular)

Pass coordinates from service to directive with angular

I made a directive for my google maps on my angular application. The latitude and longtitude are hard coded in the directive. I'm trying to get those coordinates from my service just the same as I get the location name and description.
<h2 class="page-title">{{ locationInfo.name }}</h2>
<img ng-src="{{ locationInfo.images[0].scenic}}" alt="">
<p>{{ locationInfo.description }}</p>
<gmap id ="map-canvas" class="map"></gmap>
My directive is currently looking like this which gives a static map instead of changing dynamically
.directive("gmap", function () {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function(scope, element, attrs) {
var latLng = new google.maps.LatLng(40, -73);
var mapOptions = {
zoom: 15,
center: latLng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById(attrs.id), mapOptions);
var marker = new google.maps.Marker({
position: latLng,
map: map
});
}
}
});
First, instead of using document.getElementById you should make use of the "element" atribute which you inject in your link function. That "element" is your angular object corresponding to your DOM element. If you can't use it, then go ahead using document.getElementById, but change the "id" dynamically.
Also, if you want to use it as a directive I would suggest to use two attributes for your latitude and longitude, and naming your id dynamically using some property of your locationInfo object:
<gmap id ="map-canvas_{{locationInfo.name}}" lat="locationInfo.lat" lon="locationInfo.lon" class="map"></gmap>
where locationInfo contains latitude and longitude info named as lat and lon.
Then in your directive, you read your tag atritubes via the attrs object:
.directive("gmap", function () {
return {
...
// Here you read your tag attributes "lat" and "lon"
link: function(scope, element, attrs) {
var latLng = new google.maps.LatLng(attrs.lat, attrs.lon);
// Here you can use the injected "element" instead of "document.getElementById(attrs.id)" but it seems it doesn't work for you
var map = new google.maps.Map(document.getElementById(attrs.id), mapOptions);
}
}
});
Also I recomend to define your variables using comma "," as in:
var a = something,
b = somethingElse,
c = anything;
instead of using several "var" sentences. This is a good practice.
Hope this helps.

Why in angularjs .append is acting as .html?

I am trying to append new row to a table, where existing rows are created by ng-repeat. But, when i click on add new button rater than appending a new row, it is removing the existing rows and adding new. can any one please help me in this regard ?
here is the code and fiddle link.
var app = angular.module("app", []);
app.controller("simpleController", function($scope)
{
var descriptions = ['item1','item2','tem3'];
$scope.items = descriptions;
});
app.directive("addRow",function($compile)
{
var newRow;
newRow = '<tr><td>New Description</td><td><a href="javascript:void(0)" add-row>+</a></td><td><a href="javascript:void(0)" delete-row>-</a></td></tr>';
return{
restrict: 'A',
link: function(scope, element,attrs,controller){
element.on("click", function() {
console.log("clicked on activity add row");
$compile(element.parent().parent().parent().append(newRow))(scope);
});
}
}
Whenever we want to insert new element inside angular element, it will not understand by angular directly.
First we need to run that element through compile using $compile API, then append it to element.
You need to code like below. You need to first $compile you element then append it to element.
element.on("click", function() {
console.log("clicked on activity add row");
element.parent().parent().parent().append($compile(newRow)(scope));
});
Working Fiddle
As per #huanfeng observation, <tr> is not working properly, Its get converted to span. After goggling I found, there is issue with AngularJS. For solving this you can use any of the working solution from issue link.
Update 1
Instead of appending element you can do this by more Simpler way
Only push variable inside your ng-repeat array i.e. $scope.items.push('New Description'). ng-repeat will render one more for you with your specified description.
Thanks.

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

Angularjs with leaflet directives - To clear markers

I have following setup working fine, however, I got some small problem..
Tried many ways but I cannot get rid of markers before adding new markers..
With following example, marker keep being added whenever you push from the controller..what is the best way to erase existing markers before adding any new one...?
var module = angular.module('Map', []);
module.directive('sap', function() {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function(scope, element, attrs) {
var map = L.map(attrs.id, {
center: [-35.123, 170.123],
zoom: 14
});
//create a CloudMade tile layer and add it to the map
L.tileLayer('http://{s}.tile.cloudmade.com/57cbb6ca8cac418dbb1a402586df4528/997/256/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);
//add markers dynamically
var points = [];
updatePoints(points);
function updatePoints(pts) {
for (var p in pts) {
L.marker([pts[p].lat, pts[p].long]).addTo(map).bindPopup(pts[p].message);
}
}
scope.$watch(attrs.pointsource, function(value) {
updatePoints(value);
});
}
};
});
And inside the controller, the way to add new marker is
$scope.pointsFromController.push({
lat: val.geoLat,
long: val.geoLong
});
Code in the HTML is simple
<sap id="map" pointsource="pointsFromController"></sap>
Leaflet creator here. The best way to clear markers is to add them to a group (instead of directly adding to the map), and then call group.clearLayers() when you need. http://leafletjs.com/examples/layers-control.html
Assuming pointsFromController is a simple javascript array.
You can simply undo the pushes you've made.
$scope.removePoints = function() {
for (var i=$scope.pointsFromController.length; i>=0; i--)
$scope.pointsFromController.pop();
};

Resources