I am new in AngularJS. I have searched some pages about how to manipulate DOM in directive, but most of them are manipulating the DOM of directive itself. My problem is manipulate the DOM outside of the directive.
There is a Google Map in my project, and there are some markers on the map to show events. What I want to do is that when clicking the marker, the application should show the detailed information of the event in a slide bar. Based on the code below, the requirement is when I click the marker which triggers updateMarkers() in directive, it should change the value of {{detail}} in HTML. What I am doing now is trying to use "=" to give directive access to the values in its controller scope, but this way does not work. So please offer some help if you know, thank you.
HTML:
<div id="wrapper" >
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<app-map id="map" center="loc" markers="cities" details="details" show-detail="getDetails(directivedata)" on-init="gotoCurrentLocation(directiveData)"> </app-map>
</div>
<div>**********{{details}}</div>
</div>
</div>
</div>
Part of controller:
app.controller("appCtrl", function ($scope, $http) {
$scope.nearbyEvent;
$scope.details = "something"; //should be changed when click markers in map
//code.....}
Directive:
app.directive("appMap", ['$http', function ($http) {
return {
restrict: "E",
replace: true,
template: "<div></div>",
scope: {
center: "=", // Center point on the map (e.g. <code>{ latitude: 10, longitude: 10 }</code>).
markers: "=", // Array of map markers (e.g. <code>[{ lat: 10, lon: 10, name: "hello" }]</code>).
details: "=", //used for resend a REST api and store data in here
width: "@", // Map width in pixels.
height: "@", // Map height in pixels.
zoom: "@", // Zoom level (one is totally zoomed out, 25 is very much zoomed in).
mapTypeId: "@", // Type of tile to show on the map (roadmap, satellite, hybrid, terrain).
panControl: "@", // Whether to show a pan control on the map.
zoomControl: "@", // Whether to show a zoom control on the map.
scaleControl: "@", // Whether to show scale control on the map.
onInit: "&",
showDetail: "&"
},
link: function (scope, element, attrs) {
//some code.....
function updateMarkers() {
if (map && scope.markers.length > 0) {
window.alert("get me");
// clear old markers
if (currentMarkers != null) {
for (var i = 0; i < currentMarkers.length; i++) {
currentMarkers.setMap(null);
window.alert("get here1*****");
}
}
// create new markers
currentMarkers = [];
var markers = scope.markers;
function makeHappen(thi){
return function(){
scope.details = thi;
window.alert("***" + "in directive " + thi);
}
}
for (var i = 1; i < markers.length-1; i++) {
var m = markers[i];
if(m.venue == null || m.venue.lat == 0.0)
continue;
var loc = new google.maps.LatLng(m.venue.lat, m.venue.lon);
var eventName = m.description;
var mm = new google.maps.Marker({ position: loc, map: map, title: m.name, id: m.id, count: i});
mm.addListener('click', makeHappen(eventName), false);
currentMarkers.push(mm);
}
}
}
I think this is a similar problem to the one reported here: How to access parent scope from within a custom directive *with own scope* in AngularJS?
Hope it helps!
Use
.
notation to properly enable two way data binding.
details
is a primitive type in your controller.
Though the isolate scope in directive can access
details
, when you assign something to
details
in the
makeHappen
function, it creates a new
details
in the isolate scope.
In this case, instead of a primitive type
$scope.details
in your controller, make it an object eg:
$scope.details = {msg: 'something' }
.
Print it in template ie: {{details.msg}}.
Change
details
to
details.msg
in your
makeHappen
function of your isolate scope.
Sorry for my mistake. Looks like, you don't need to change scope.details
from string to object. The below changes would have been enough for it to work.
function makeHappen(thi){
return function(){
scope.$apply(function() {
scope.details = thi;
})
}
}
For isolate scope, whenever a shared variable(controller scope var shared with directive scope) is changed in the directive scope, the variable in controller also gets updated. It doesn't matter whether it is primitive or object because there is no prototypical inheritance between the directive scope and controller scope. However, if the directive's scope property was true( scope: true
) rather than isolate scope, then scope.details had to be an object to see the changed value in the controller scope(that I explained earlier).
The only change that's required is to wrap the scope.details = thi
inside the scope.$apply(function(){//code})
or to call scope.$apply()
at the end of the makeHappen return function.
So, the question is, why do you need to call scope.$apply()
or put certain code inside scope.$apply(function() {//code})
? The reason is:
$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
In your case, you attached an event listener mm.addListener('click', makeHappen(eventName), false);
. The makeHappen
function gets called whenever marker is clicked. It happens outside angular context. Angular doesn't know that scope.details
value is changed. So, the watcher for scope.details
doesn't get called and hence, you don't see any update. And, here comes the solution scope.$apply()
. Whenever you call scope.$apply()
, it internally calls scope.$digest()
which actually update any bindings or watchers.
So, when do you need to call scope.$apply()
? Behind the scene, Angular wraps almost all of the code within $apply() that you write in context of angular. Event like ng-click
or $timeout
or $http
callbacks are wrapped inside scope.$apply()
so that you don't need to call it explicitly. You will get error if you call $apply yourself.
For more info on $apply, you can visit this links: https://docs.angularjs.org/api/ng/type/ $rootScope.Scope#$apply http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
I have made a plunk for you to better understand the different scope scenarios in directive and why $apply
is needed in some cases. Please, have a look at this plunk. http://plnkr.co/edit/96a646T6FuBhmjdLgJXq?p=preview
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.