0

I am trying to initialize some data in an AngularJS directive with child elements. In this case, the <map> element is replaced with a <div> that gets hooked up using leaflet. I was trying to figure out if there is some way in the directive compile or link function to populate the markers collection from declared child elements, something like this:

<div ng-app="dashboard"> <div ng-controller="DashboardCtrl"> <map id="devicemap" tile-handle="test.map-fy18v14h" min-zoom="3" max-zoom="9" markers="markers"> <marker lat="44" lng="-88.5" description="From the Server (Razor)" /> <marker lat="44.1" lng="-88.6" description="From the Server 2 (Razor)" /> </map> </div> 

In the directive, I would like iterate over the <marker> elements to populate a collection. Should this happen in compile? Or, am I misguided that I can access my "fake DOM" prior to the actual template being inserted?

module.directive('map', function () { return { restrict: "E", replace: true, scope: { markers: "=markers" }, template: '<div class="map"></div>', link: function link(scope, elm, attributes) { var map = L.map(attributes.id, { dragging: true, zoomAnimation: true }).setView([45.505, -88.09], 6); L.tileLayer('http://{s}.tiles.mapbox.com/v3/' + attributes.tileHandle + '/{z}/{x}/{y}.png', { attribution: "<a href='http://mapbox.com/about/maps' target='_blank'>Terms & Feedback</a>", }).addTo(map); // This is where I am stuck... is there a way to get the "marker" elements? // the following does not work... var markerElements = elm.children('marker'); // now would like to loop over markerElements, grab the attributes and add them // to the map (and/or the markers collection). }; }); 

I am able to populate the markers using an ajax call, however, this technique would allow me to pre-populate the data on the server when the page is first requested.

3
  • Because you are using a template (and replace: true), by the time the link function runs, the <marker> tags have been removed. That's why the link function can't find them. Commented Mar 28, 2013 at 18:45
  • That makes sense. Is there a way to access the original DOM before it gets replaced? Commented Mar 28, 2013 at 18:54
  • 1
    You can do that in the compile function, but you can't use template (or replace: true), otherwise the compile function will see the applied template rather than the original DOM. Inside the compile function you can use replaceWith() when you're done modifying the DOM. Example: stackoverflow.com/a/10646761/215945 Commented Mar 28, 2013 at 19:43

2 Answers 2

0

Ok, with the hint from Mark Rajcok, I have come up with a solution. This allows me to set up the markers on the map using custom HTML elements that are generated on the server. I needed to use the list variable to store off the data collected in the compile function. Not sure if there is a cleaner way to do this with Angular.

HTML/Razor

<div ng-app="dashboard"> <div ng-controller="DashboardCtrl"> <map id="devicemap" tile-handle="example.map-fy18v14h" min-zoom="3" max-zoom="9" markers="markers"> @foreach (var location in Model.Locations) { <marker lat="@location.Lat" lng="@location.Lng" description="@location.Description" ></marker> } </map> </div> </div> 

Controller

var module = angular.module('dashboard', ['map-directive']); module.controller("DashboardCtrl", function ($scope, $http) { $scope.markers = []; }); 

Directive

var mapDirective = angular.module("map-directive", []); mapDirective.directive('map', function () { var list = []; function link (scope, elm, attributes) { var map = L.map(attributes.id, { dragging: true, zoomAnimation: true }).setView([45.505, -88.09], 6); scope.markers = list; L.tileLayer('http://{s}.tiles.mapbox.com/v3/' + attributes.tileHandle + '/{z}/{x}/{y}.png', { attribution: "<a href='http://mapbox.com/about/maps' target='_blank'>Terms & Feedback</a>", }).addTo(map); scope.$watch('markers', function (newValue, oldValue) { if (newValue) angular.forEach(scope.markers, function (marker) { L.marker([marker.lat, marker.lng], { draggable: marker.draggable }).addTo(map).bindPopup(marker.description); }); }); } return { restrict: "E", scope: { markers: "=markers" }, compile: function(tElement, tAttrs, transclude) { console.log(tElement); console.log(tElement.find("marker")); var markers = tElement.find("marker"); angular.forEach(markers, function (marker) { list.push({ lat: marker.attributes.lat.value, lng: marker.attributes.lng.value, description: marker.attributes.description.value}); }); var htmlText = '<div class="map" id="' + tAttrs.id + '" ></div>'; tElement.replaceWith(htmlText); return link; } }; }); 
Sign up to request clarification or add additional context in comments.

1 Comment

list will be shared by all map directives in the app. If you plan on using more than one map, then you'll need to have multiple lists, or convert the list from an array to an object, where each property is the map's id, and each property value is an array.
0

Actually you can use derective with templateUrl. Just put ng-transclude on any element there and it will have all original children elements as its own. Then in link function you can read those DOM elements, do whatever you ant and clean them up.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.