谷歌地图信息窗口内的AngularJS ng-include?
Posted
技术标签:
【中文标题】谷歌地图信息窗口内的AngularJS ng-include?【英文标题】:AngularJS ng-include inside of Google Maps InfoWindow? 【发布时间】:2012-12-23 00:19:40 【问题描述】:我正在尝试将模板文件 views/infowindow.html
作为我为启动 google maps api 而编写的服务中的 InfoWindow 的内容:
for ( var count = locations.length, i = 0; i < count; i++ )
var latLng = locations[i],
marker = new google.maps.Marker(
…
),
infowindow = new google.maps.InfoWindow();
google.maps.event.addListener(
marker,
'click',
(function( marker , latLng )
return function()
var content = '<div ng-include src="\'infowindow.html\'"></div>';
infowindow.setContent( content );
infowindow.open( Map , marker );
//return fn()
)( marker , latLng )
);//addListener
//for
但是,当将content
插入信息窗口时,Angular 似乎没有处理它(通过开发工具检查代码时,插入的代码是<div ng-include src="'views/infowindow.html'"></div>
)。
我希望 Angular 在将我的包含插入到 InfoWindow 之前对其进行预处理,但可惜没有。
我正在尝试做的事情可能吗?
我想在将模板传递给 infowindow.setContent()
之前,我必须以某种方式缓存模板,但我不知道该怎么做(或者我什至应该这样做正在做)。我宁愿在事件上加载模板,而不是为每个标记缓存和注入它。
编辑查看 $templateCache 和 related SO question。
EDIT 2这是一个尝试使用$compile
的plunk(InfoWindow的内容仍然是<div id="infowindow_content" ng-include src="'infowindow.html'"></div>
)
解决方案
这个基础来自Mark's answer below。在他的解决方案中,InfoWindow 的内容在第一次单击(任何标记)时编译,但 InfoWindow 直到再次单击任何标记时才真正打开,可能是因为 GoogleMaps 不耐烦。
将$compile
移到外面,然后将编译后的模板传入.addListener
就可以解决这个问题:
for ( … )
…
infowindow = new google.maps.InfoWindow();
scope.markers …
var content = '<div id="infowindow_content" ng-include src="\'infowindow.html\'"></div>';
var compiled = $compile(content)(scope);
google.maps.event.addListener(
marker,
'click',
(function( marker , scope, compiled , localLatLng )
return function()
scope.latLng = localLatLng;//to make data available to template
scope.$apply();//must be inside write new values for each marker
infowindow.setContent( compiled[0].innerHTML );
infowindow.open( Map , marker );
;//return fn()
)( marker , scope, compiled , scope.markers[i].locations )
);//addListener
//for
Updated Plunker.
【问题讨论】:
您好。我想知道为什么打开另一个信息窗口时您的信息窗口会关闭?我必须使用相同的信息窗口来实现这一点。似乎每个人都有一个,但它们仍然关闭。 @Hawk,我不明白你的问题。我不再使用 InfoWindow(我使用 InfoBox),所以我可能无法为您提供帮助。 @jacob 你可能应该使用infowindow.setContent( compiled[0]);
而不是infowindow.setContent( compiled[0].innerHTML );
@theres,是的,这可能更好。
有没有办法在 infowindow.open() 之后保留双向绑定?我想让模板对范围内的更改做出反应。例如,我在您的示例中添加了一个 setInteral 调用来修改范围,但此更改不会自动反映在打开的 infoWindow 上。见:plnkr.co/edit/Ix92XXSqxru8eMtmCdgH?p=preview
【参考方案1】:
在将 content
添加到 DOM 后,您需要找到它(可能使用 jQquery 选择器?),然后找到 $compile
() 并将其应用于适当的范围。这将导致 Angular 解析您的内容并根据它找到的任何指令(如 ng-include)采取行动。
例如,$compile(foundElement)(scope)
没有更多的代码,很难给出更准确的答案。不过,这里有一个similar question and answer,你可以看看。
更新:好的,我终于开始工作了,我学到了一些东西。
google.maps.event.addListener(
marker,
'click',
(function( marker , scope, localLatLng )
return function()
var content = '<div id="infowindow_content" ng-include src="\'infowindow.html\'"></div>';
scope.latLng = localLatLng;
var compiled = $compile(content)(scope);
scope.$apply();
infowindow.setContent( compiled[0].innerHTML );
infowindow.open( Map , marker );
;//return fn()
)( marker , scope, scope.markers[i].locations )
我的印象是只有 DOM 元素可以被编译——也就是说,我首先必须将内容添加到 DOM,然后编译它。事实证明这不是真的。上面,我首先针对scope
编译content
,然后将其添加到DOM。 (我不知道这是否会破坏数据绑定——即由 $compile 设置的 $watch()es。)我必须设置 scope.latLng
因为包含 ng 的模板需要插入 latLng[0]
和latLng[1]
。我使用了innerHTML
而不是outerHTML
,因此只插入了 infowindow.html 的内容。
Plunker.
Update2:第一次点击不起作用。似乎直到第二次单击才加载“infowindow.html”(我尝试再次调用 scope.$apply() ......没有帮助)。当我让 plunker 工作时,我在 index.html 中内联了 infowindow.html 的内容:
<script type="text/ng-template" id="/test.html">
<h4>latLng[0],latLng[1]</h4>
</script>
我在 addListener() 中使用它:
var content = '<div id="infowindow_content" ng-include src="\'/test.html\'"></div>';
我将 plunker 更改为使用内联模板。
【讨论】:
嗨,马克,看起来它可能有效,谢谢!顺便说一句,你想看什么额外的代码? (为了测试,infowindow.html
的内容是<h3>Foo</h3>
)。我在答案中的代码中添加了更多内容,但这就是全部(没什么特别的)。
@jacob,你显示的代码在哪里?它在 Angular 服务、指令、控制器中吗?由于您正在执行 DOM 操作,因此它应该在指令中。此外,尚不清楚我们可以编译什么元素......即,我上面的答案中的“foundElement”将是什么。好吧,我去快速查看了 Google API 并且(至少在我看到的一个示例中),setContent() 或 open()(不确定是哪个)使用id="content"
创建了一个 div,所以你应该能够找到那个元素并 $compile 它。
地图是在服务中启动的(我在问题中添加了一个plunk)。据我通过开发工具看到的,gMaps api 默认情况下不会使用id="content"
创建一个 div。
如果 Angular 没有注意到 ngInclude
的原样,为什么它会注意到我创建的呢?或者你是说整个服务真的应该是一个指令?
我一直在研究这个,但进展不大。一些注意事项: scope.$apply() 必须在 $compile 之后调用; addListener() 被传递 scope.markers[i]
而不是 scope
(?);是的,使用 infowindow_content (不是内容)来查找要 $compile 的元素;它不必在指令中工作,只是通常 DOM 操作是由指令执行的,而不是服务。【参考方案2】:
以上答案是可靠的,但您失去了对以下方面的约束:
infowindow.setContent( compiled[0].innerHTML );
改为这样做:
infowindow.setContent( compiled[0] );
否则,如果您有<div>myVar</div>
之类的内容,如果您的应用中更新了myVar
,则它不会更新。
【讨论】:
这非常重要,因为除了使用 ng-include 之外,上述答案对任何事情都没有用。【参考方案3】:你试过编译功能吗? http://docs.angularjs.org/api/ng.$compile
我还没有深入研究 Angular,但我认为这可行。
或者,您可以尝试引导这些东西。但我不相信这是正确的方法...http://docs.angularjs.org/guide/bootstrap
【讨论】:
嗨卢克,所以我会这样做并缓存“编译”输出并将其注入.addListener()
函数?以上是关于谷歌地图信息窗口内的AngularJS ng-include?的主要内容,如果未能解决你的问题,请参考以下文章