如何在Aurelia中使用BingMaps

时间:2016-12-15 10:42:19

标签: typescript webpack bing-maps aurelia

我正在为我的应用程序使用aurelia skeleton typescript webpack模板。 我想将BingMaps添加到页面中,并能够添加图钉等。 到目前为止,我已经做到了这一点:

  

index.html - 我添加了一个脚本标记,用于从CDN加载地图

<head>
...
   <script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol'></script>
</head>

然后我添加了这样一个模板:

  

map.html

<template>
   <div id='mainMap' style='width: 100vw; height: 100vh;'></div>
</template>

然后我有地图控制器:

  

map.ts

export class Map {

    private map: Microsoft.Map.Map;

    attached() {
        this.map = new Microsoft.Maps.Map('mainMap', { credentials:'mycredentials - omitted'});
        ...
    }
}

现在,如果我启动应用程序,将显示welcome screent(来自骨架)。然后我点击地图&#39;菜单链接和地图页面显示与地图完全加载。 但是,如果我在浏览器中单击刷新(F5或Ctrl + F5),则不再显示地图,并且控制台中会显示错误:

  

bluebird.js:1546未处理的拒绝TypeError:无法读取属性&#39; prototype&#39;为null       在k(https://www.bing.com/mapspreview/sdk/mapcontrol:11:7096)       在h(https://www.bing.com/mapspreview/sdk/mapcontrol:11:6285)       在e(https://www.bing.com/mapspreview/sdk/mapcontrol:11:1106)       at t.l [as instance](https://www.bing.com/mapspreview/sdk/mapcontrol:11:161)       在h(https://www.bing.com/mapspreview/sdk/mapcontrol:11:6042)       在e(https://www.bing.com/mapspreview/sdk/mapcontrol:11:1106)       at t.l [as instance](https://www.bing.com/mapspreview/sdk/mapcontrol:11:161)       在新的Microsoft.Maps.Map(https://www.bing.com/mapspreview/sdk/mapcontrol:13:4304)       在Map.attached(http://localhost:9000/app.bundle.js:31267:20)       在Controller.attached(http://localhost:9000/aurelia.bundle.js:6438:22)       在View.attached(http://localhost:9000/aurelia.bundle.js:4524:23)       在ViewSlot.attached(http://localhost:9000/aurelia.bundle.js:4883:13)       在View.attached(http://localhost:9000/aurelia.bundle.js:4534:19)       在ViewSlot.attached(http://localhost:9000/aurelia.bundle.js:4883:13)       在http://localhost:9000/aurelia.bundle.js:14717:28

尝试在地图控制器的附加事件中实例化Map对象时抛出此错误。

为什么会发生这种情况,我该如何解决这个问题? 请帮忙

由于

2 个答案:

答案 0 :(得分:2)

解决此问题的关键是使用API​​允许我们在脚本网址中指定的callback参数。我已经创建了一个自定义元素来执行此操作。最初加载模块时会加载脚本。自定义元素的任何实例都将等待调用回调函数。我最初使用的是一个带有MutationObserver和数据属性的复杂设置,但在与Jeremy Danyow交谈之后,他指出&#34; Promisifying&#34;回调将更简单地解决解决方案。这个更简单的解决方案如下所示。

除了获取地图的当前中心点之外,自定义元素目前还没有提供任何与地图交互的API,但它是一个很好的起点来帮助您。

<强>丙map.ts

import { bindable, bindingMode, inlineView } from 'aurelia-framework';

const controlUrl = '//www.bing.com/api/maps/mapcontrol?callback=bingMapsLoaded';
const ready = new Promise(resolve => window['bingMapsLoaded'] = resolve);

let scriptTag: HTMLScriptElement = document.createElement('script');

scriptTag.async = true;
scriptTag.defer = true;
scriptTag.src = controlUrl;

document.head.appendChild(scriptTag);

@inlineView('<template><div ref="container" css="width: ${width}; height: ${height};"></div></template>')
export class BingMapCustomElement {
  private container: HTMLElement;
  private map: Microsoft.Maps.Map;
  private viewChangeHandler: Microsoft.Maps.IHandlerId;

  @bindable apiKey = '';
  @bindable height = '600px';
  @bindable width = '400px';

  @bindable({ defaultBindingMode: bindingMode.twoWay }) location: Microsoft.Maps.Location | string;

  attached() {
    return ready.then(() => {
      this.map = new Microsoft.Maps.Map(this.container as HTMLElement, {
        credentials: this.apiKey
      });

      this.location = this.map.getCenter();

      this.viewChangeHandler = Microsoft.Maps.Events.addHandler(this.map, 'viewchange', e => {
        this.location = this.map.getCenter();
      });
    });
  }

  detached() {
    if (this.viewChangeHandler) {
      Microsoft.Maps.Events.removeHandler(this.viewChangeHandler);
    }

    if (this.map) {
      this.map.dispose();
      this.map = null;
    }
  }
}

用法

<bing-map api-key.bind="mapsApiKey" width="100px" height="100px"></bing-map>

答案 1 :(得分:1)

这是因为您在外部脚本完全加载之前尝试实例化地图。它适用于您的第一个场景,但在您直接刷新时不起作用。

这里有一个很好的解决方案:

How to wait BingMaps to be loaded in Aurelia

回调解决方案的问题在于您在index.html中加载外部脚本,这对您当前是否要显示地图视而不见。所以第二种解决方案更合适。但是,我认为该解决方案是为ESNext代码编写的,并且您使用的是TypeScript,这意味着typeof属性永远不会被定义。以下 map.ts 的代码在您的上下文中可以更好地工作(您可能需要稍微调试一下,因为我无法对其进行测试):

BEGIN TRANSACTION;

SELECT ITEM_ID
FROM TABLE_ITEM
WITH(XLOCK, ROWLOCK)
WHERE ITEM_PRIORITY > 10 AND ITEM_CATEGORY = 'CT1' AND ITEM_STATUS = 'available' AND ROWNUM = 1;

UPDATE [locked item_id] SET ITEM_STATUS = 'unavailable';

COMMIT TRANSACTION;

请注意export class Map { map:Microsoft.Maps.Map; attached() { this.loadMap(); } loadMap() { if ((Microsoft == null) || (Microsoft.Maps == null)) { // not yet available, keep trying (dirty checking) setTimeout(this.loadMap, 100); } else { // Map API available; proceed to render the map this.map = new Microsoft.Maps.Map('#mainMap', {credentials: myKey}); this.map.setView({center: new Microsoft.Maps.Location(45.093,14.114), zoom:15}); } } } 测试,而不是if ((Microsoft == null) || (Microsoft.Maps == null))测试。

另一个很好的解决方案(由Jason Sobell提供)

http://www.sobell.net/calling-external-javascript-from-within-aurelia-templates/

第三个很好的解决方案(经过一些回调研究)

您可以在undefined中实现此而无需外部脚本链接,因为这应该动态地提供加载机制。

index.html