【问题标题】:How to load google maps api asynchronously in Angular2如何在Angular2中异步加载谷歌地图api
【发布时间】:2016-01-21 18:41:13
【问题描述】:

通常在纯 javascript 网站中,我可以使用以下脚本来引用 google maps api 并将 callback 函数设置为 initMap

<script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>

我观察到的是普通javascript站点中的initMap函数在window范围内,可以在脚本参数设置中引用-?callback=initMap,但是一旦我用组件方法在angular2中写了一个组件称为initMapinitMap 将在我的组件范围内。那么我在索引中设置的异步加载脚本将无法捕捉到我的组件initMap方法。

具体来说,我想知道如何在Angular2实现同样的事情?

PS:我知道alpha 中通过npm 提供了一个angular2-google-maps 组件,但目前它的功能有限,所以我想知道如何在不使用的情况下以更简单的方式加载它另一个组件,所以我可以使用 google maps api 来实现我的项目。

【问题讨论】:

    标签: google-maps angular


    【解决方案1】:

    我知道您不想要另一个组件,但是聚合物的组件可以很好地与 google apis 配合使用。我有使用聚合物 youtube 数据 api 的 angular2 代码。我得到了帮助来设置它。这是让我开始的plunker。我认为最难的部分是为回调设置,我相信你可以在没有聚合物的情况下做到这一点。该示例显示了使用 Angular 服务连接所有内容的棘手部分。

        const url = 'https://apis.google.com/js/client.js?onload=__onGoogleLoaded'
    
        export class GoogleAPI {
          loadAPI: Promise<any>
          constructor(){
            this.loadAPI = new Promise((resolve) => {
              window['__onGoogleLoaded'] = (ev) => {
                console.log('gapi loaded')
                resolve(window.gapi);
              }
              this.loadScript()
            });
            
          }
          
          doSomethingGoogley(){
            return this.loadAPI.then((gapi) => {
              console.log(gapi);
            });
          }
          
          loadScript(){
            console.log('loading..')
            let node = document.createElement('script');
            node.src = url;
            node.type = 'text/javascript';
            document.getElementsByTagName('head')[0].appendChild(node);
            
          }
        }

    【讨论】:

    • 这正是我想要的。这是不言自明的。谢谢!
    • 在 gmaps 异步加载上正常工作。谢谢!
    • 我刚刚尝试了代码,但 VS 说:“窗口”类型上不存在属性“gapi”。
    • 你可以使用 window['gapi'] 代替 window.gapi
    • 使用 Typescript,如果您在类上方声明窗口对象,错误消息应该会消失:declare var window: any;
    【解决方案2】:

    我在尝试开发渐进式网络应用程序时遇到了这个问题,即有可能不在线的地方。代码示例中有一个错误:谷歌地图脚本中的onload 应该是callback。所以我对user2467174的修改导致

    ma​​p-loader.service.ts

    const url = 'http://maps.googleapis.com/maps/api/js?key=xxxxx&callback=__onGoogleLoaded';
    
    @Injectable()
    export class GoogleMapsLoader {
        private static promise;
    
        public static load() {
            // First time 'load' is called?
            if (!GoogleMapsLoader.promise) {
    
                // Make promise to load
                GoogleMapsLoader.promise = new Promise( resolve => {
    
                    // Set callback for when google maps is loaded.
                    window['__onGoogleLoaded'] = (ev) => {
                        resolve('google maps api loaded');
                    };
    
                    let node = document.createElement('script');
                    node.src = url;
                    node.type = 'text/javascript';
                    document.getElementsByTagName('head')[0].appendChild(node);
                });
            }
    
            // Always return promise. When 'load' is called many times, the promise is already resolved.
            return GoogleMapsLoader.promise;
        }
    }
    

    然后我有一个带有

    组件
    import { GoogleMapsLoader } from './map/map-loader.service';
    constructor() {
    
        GoogleMapsLoader.load()
        .then(res => {
            console.log('GoogleMapsLoader.load.then', res);
            this.mapReady = true;
        })
    

    还有一个模板

    <app-map *ngIf='mapReady'></app-map>
    

    这样map div只有在在线时才会放入dom中。

    然后在 ma​​p.component.ts 中,我们可以等到组件放入 DOM 后再加载地图本身。

    ngOnInit() {
        if (typeof google !== 'undefined') {
            console.log('MapComponent.ngOnInit');
            this.loadMap();
        }
    }
    

    【讨论】:

      【解决方案3】:

      以防万一你想让它成为一个静态函数,它总是返回一个承诺,但只获取一次 api。

      const url = 'https://maps.googleapis.com/maps/api/js?callback=__onGoogleMapsLoaded&ey=YOUR_API_KEY';
          
      export class GoogleMapsLoader {
        private static promise;
        public static load() {
      
          // First time 'load' is called?
          if (!GoogleMapsLoader.promise) {
      
            // Make promise to load
            GoogleMapsLoader.promise = new Promise((resolve) => {
      
              // Set callback for when google maps is loaded.
              window['__onGoogleMapsLoaded'] = (ev) => {
                console.log('google maps api loaded');
                resolve(window['google']['maps']);
              };
      
              // Add script tag to load google maps, which then triggers the callback, which resolves the promise with windows.google.maps.
              console.log('loading..');
              let node = document.createElement('script');
              node.src = url;
              node.type = 'text/javascript';
              document.getElementsByTagName('head')[0].appendChild(node);
            });
          }
      
          // Always return promise. When 'load' is called many times, the promise is already resolved.
          return GoogleMapsLoader.promise;
        }
      }
      

      这是在其他脚本中获取 api 的方法:

      GoogleMapsLoader.load()
        .then((_mapsApi) => {
          debugger;
          this.geocoder       = new _mapsApi.Geocoder();
          this.geocoderStatus = _mapsApi.GeocoderStatus;
        });
      

      【讨论】:

        【解决方案4】:

        这是我目前正在使用的:

        loadMapsScript(): Promise<void> {
          return new Promise(resolve => {
            if (document.querySelectorAll(`[src="${mapsScriptUrl}"]`).length) {
              resolve();
            } else {
              document.body.appendChild(Object.assign(document.createElement('script'), {
                type: 'text/javascript',
                src: mapsScriptUrl,
                onload: doMapInitLogic();
              }));
            }
          });
        }
        

        看我更全面的说明here

        【讨论】:

        • 你能提供一个完整的例子吗?如果我有依赖于谷歌地图的 3rd 方库(即 js-map-label),它将如何工作。也许this供参考
        • @MohammadAli,我在答案中提供了一个链接。我建议在路由解析器中加载这两个脚本。首先加载地图脚本,然后加载第 3 方脚本。比如:loadGoogleMapsScript().switchMap(() =&gt; loadThirdPartyScript()));
        • 感谢您的回复。谷歌地图有 url,所以我可以使用上面的代码,但是如果 3rd 方插件在节点模块文件夹中,我将如何处理 loadThirdPartyScript 逻辑?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-14
        • 2011-05-19
        • 2015-07-03
        • 1970-01-01
        • 1970-01-01
        • 2014-07-13
        相关资源
        最近更新 更多