如何在Leaflet标记弹出窗口中使用Angular指令ng-click和ng-class

17

我正在使用Angular.JS和Leaflet.JS为我的位置地图添加具有弹出窗口绑定的地图标记。 我需要使用一个带有两个不同图标的span标签(其中一个在下面的代码中显示),您可以单击它们以调用不同的函数,并使用ng-class更改类别,如果满足某些条件。 这是我的代码:

var marker = L.marker([51.5, -0.09], {icon: blueIcon}).bindPopup('<br><span ng-class="thumbsUpClass(' + hotelsSelectedDates[i]['hotels'][s] + ')" ng-click="addChoice(' + hotelsSelectedDates[i]['hotels'][s] + ',' + hotels + ')"><span class="popup-container"><span class="icon-stack thumbs-up-stack"><i class="icon-sign-blank icon-stack-base"></i><i class="icon-thumbs-up"></i></span></span></span>');

然而,当我检查该元素时,我得到了以下内容:

<span ng-class="thumbsUpClass([object Object])" ng-click="addChoice([object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object])"><span class="popup-container"><span class="icon-stack thumbs-up-stack"><i class="icon-sign-blank icon-stack-base"></i><i class="icon-thumbs-up"></i></span></span></span>

ng-click应该将特定对象和对象数组都发送到该函数,但当我点击图标时,什么也不会发生。在我的研究中,我发现弹出窗口会阻止事件传播(更多信息),但我不确定如何覆盖它或修复它以使其与Angular一起正常工作。有人知道如何实现吗?

更新:

由于ng-click / class评估字符串,因此我将变量修复为以下格式:

$scope.item = hotelsSelectedDates[i]['hotels'][s]
$scope.set = hotels
var marker = L.marker([51.5, -0.09], {icon: blueIcon}).bindPopup('<br><span ng-class="thumbsUpClass(item)" ng-click="addChoice(item,set)"><span class="popup-container"><span class="icon-stack thumbs-up-stack"><i class="icon-sign-blank icon-stack-base"></i><i class="icon-thumbs-up"></i></span></span></span>');

HTML会正确地呈现出来:

<span ng-class="thumbsUpClass(item)" ng-click="addChoice(item,set)"><span class="popup-container"><span class="icon-stack thumbs-up-stack"><i class="icon-sign-blank icon-stack-base"></i><i class="icon-thumbs-up"></i></span></span></span>

但是当我点击图标时,什么也没有发生,似乎这些功能没有被调用。有人知道为什么会发生这种情况吗?


1
尝试让您的代码示例适应页面宽度,这样我们就不必滚动或使用 Plunkr,否则需要花费很多精力才能阅读。 - Maarten
6个回答

28

你遇到的问题是由于你手动创建了一些DOM元素,这些元素并未被AngularJS编译。

在这种情况下,你需要手动编译和链接该元素。

代码应该如下所示:

var html = '<br><span ng-class="thumbsUpClass(item)" ' +
    'ng-click="addChoice(item,set)"><span class="popup-container"><span ' +
    'class="icon-stack thumbs-up-stack"><i class="icon-sign-blank ' +
    'icon-stack-base"></i><i class="icon-thumbs-up"></i></span></span></span>',
    linkFunction = $compile(angular.element(html)),
    newScope = $scope.$new();

newScope.item = hotelsSelectedDates[i]['hotels'][s]
newScope.set = hotels
var marker = L.marker([51.5, -0.09], {icon: blueIcon}).bindPopup(linkFunction(newScope)[0]);

在这里,我拿到你的HTML字符串,首先将其转换为DOM。因为AngularJS需要处理的是DOM而不是字符串。

angular.element(html)
然后,我使用$compile服务将这个DOM编译成一个链接函数。
linkFunction = $compile(angular.element(html));
当执行此函数时,它将返回一个由Angular完全控制的jQuery DOM树,并在您作为参数提供的范围内运行。这就是我在这里所做的事情。
linkFunction(newScope)
请注意,我给出的范围是$scope的子范围。如果不这样做,您将在所有弹出窗口之间共享同一个作用域,这不是一个好主意。创建新作用域是在var声明中完成的。
newScope = $scope.$new()

通过这样可以获取实际的DOM节点

linkFunction(scope)[0]

然后将它传递给Leaflet

.bindPopup(linkFunction(newScope)[0]);

搞定了!

欲知详情,请参阅编译器文档。

编辑:有关作用域的问题已得到纠正。


10

您可以在angular-leaflet-directive中使用对Angular内容的新支持:

var html = '<br><span ng-class="thumbsUpClass(item)" ' +
    'ng-click="addChoice(item,set)"><span class="popup-container"><span ' +
    'class="icon-stack thumbs-up-stack"><i class="icon-sign-blank ' +
    'icon-stack-base"></i><i class="icon-thumbs-up"></i></span></span></span>';

... 

$scope.markers.push( { lat: ...,
                       lng: ...,
                       message: html,
                       getMessageScope: function() { return $scope; },          
});

1
这个非常好用!我想知道为什么网站上没有例子... 然而,在插件的最新版本中,你传递给 push 方法的对象中可能还需要将 compileMessage 设置为 true。此外,这只适用于标记,而不是路径等其他内容。 - sibbl

1
我发现这个回答对于mapbox-gl-js和angular很有帮助:
var html = '<button ng-click="fn()">Click Me</button>';
var compiledHtml = $compile(html)($scope);
var popup = new mapboxgl.Popup()
 .setLngLat([-91.874, 42.760])
 .setDOMContent(compiledHtml[0])
 .addTo(map);

在弹出窗口(mapbox)上启用ng-click


0

虽然我来晚了,但我遇到过类似的情况,想分享一下我的解决方案。

假设你有一些 HTML 代码要添加到你的弹窗中,只需在其中添加一个“id”即可。

var html = '<a id="popup" href="" ng-model="featureSelected" ng-click="cherryPickPin(featureSelected)">cherry pick this pin</a>';

现在你需要做的就是:

var popupElement = $('#popup'); // grab the element (make sure that it exists in the DOM)
popupElement = $compile(popupElement)($scope); // let angular know about it

0
我曾经处理过这种特定类型的问题,即在弹出窗口内实例化一个函数以便对单击事件进行响应。当时我使用的是 angular-leaflet-directive,但由于它已经处于 $compile 内部,所以我无法进行 $compile 操作,否则会产生循环引用。因此,我采取了以下措施。
$scope.markers[$scope.asset_table[imc]['asset']['_id']['$oid']]={
                        lat:$scope.asset_table[imc]['last_data']['loc']['coordinates'][1],
                        lng:$scope.asset_table[imc]['last_data']['loc']['coordinates'][0],
                        focus:true,
                        message:"<h4>"+$scope.asset_table[imc]['asset']['name']+"</h4>
                        <span class='btn btn-danger' 
                        onclick='detailView()';>View Details</span>",
                        //message: linkFunction, /*I discarded this as it was creating circular reference*/
                        icon: {
                            iconUrl: $scope.icon,
                            iconSize: [30, 30],
                            iconAnchor: [15, 15],
                            popupAnchor: [0, 0],
                            shadowUrl: $scope.icon,
                            shadowSize:[0,0],
                            iconAngle:$scope.asset_table[imc]['last_data']['bearing']
                        }
                    };

而在下面,函数viewDetail()被调用了

detailView = function(){
    //alert("Test");
    $rootScope.$broadcast('asset.details',$scope.asset_table);
}

所以我不得不在 Angular 中使用传统的 JavaScript,以避免循环链接的复杂性。

不确定你在这里做了什么。你如何从你的资产表中获取与特定弹出窗口相关的数据? - mstreffo
每个asset_table [imc]都有一个独特的消息,其中包含一个基于span的引导式按钮,其使用onclick而不是ng-click触发detailView()函数,该函数是用纯javascript编写的。这将触发广播。 - tor9ado

0

我来到这篇文章是因为我在寻找一个关于移动应用程序中Ionic/Cordova(地图集成)的问题解决方案。

问题在于$compute函数似乎无法工作,因为它使用了jQuery的lite版本。所以唯一的方法,也显然是最简单的方法,就是使用David提出的代码(非常感谢你,伙计!)。

在我的代码中,它看起来像这样:

$scope.map.markers[i] = {
    lat: parseFloat(place.latitude),
    lng: parseFloat(place.longitude),
    message: htmlPopupContent,
    getMessageScope: function() { return $scope; },
    focus: true,
    draggable: false
};

希望这能有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接