谷歌地图信息窗口内的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 似乎没有处理它(通过开发工具检查代码时,插入的代码是&lt;div ng-include src="'views/infowindow.html'"&gt;&lt;/div&gt;)。

我希望 Angular 在将我的包含插入到 InfoWindow 之前对其进行预处理,但可惜没有。

我正在尝试做的事情可能吗?

我想在将模板传递给 infowindow.setContent() 之前,我必须以某种方式缓存模板,但我不知道该怎么做(或者我什至应该这样做正在做)。我宁愿在事件上加载模板,而不是为每个标记缓存和注入它。

编辑查看 $templateCache 和 related SO question。

EDIT 2这是一个尝试使用$compile的plunk(InfoWindow的内容仍然是&lt;div id="infowindow_content" ng-include src="'infowindow.html'"&gt;&lt;/div&gt;


解决方案

这个基础来自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 的内容是&lt;h3&gt;Foo&lt;/h3&gt;)。我在答案中的代码中添加了更多内容,但这就是全部(没什么特别的)。 @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] );

否则,如果您有&lt;div&gt;myVar&lt;/div&gt; 之类的内容,如果您的应用中更新了myVar,则它不会更新。

【讨论】:

这非常重要,因为除了使用 ng-include 之外,上述答案对任何事情都没有用。【参考方案3】:

你试过编译功能吗? http://docs.angularjs.org/api/ng.$compile

我还没有深入研究 Angular,但我认为这可行。

或者,您可以尝试引导这些东西。但我不相信这是正确的方法...http://docs.angularjs.org/guide/bootstrap

【讨论】:

嗨卢克,所以我会这样做并缓存“编译”输出并将其注入.addListener() 函数?

以上是关于谷歌地图信息窗口内的AngularJS ng-include?的主要内容,如果未能解决你的问题,请参考以下文章

如何在谷歌地图上显示多个信息窗口?

显示信息窗口后阻止谷歌地图移动

谷歌地图的自定义信息窗口

保存按钮未附加到右侧信息窗口,谷歌地图

无法单击标记 - 谷歌地图集群Android,无法在谷歌地图中显示信息窗口

通过单击地图关闭谷歌地图中的信息窗口?