13

I want to add Bing Map V8 control to my Anguar 2.0 project. I want to know what I need to do to add Bing Map V8 into Angular 2.0 project. I have attached my implementation. The component I created couldn't be loaded. How do I reference Microsoft.Maps.Map?

Here is an example of the bing map v8. Everything works well if saving the following example as HTML. The bing map key was clipped.

<!DOCTYPE html>
<html>
    <head>
        <title>addOneLayerItemHTML</title>
        <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
    </head>
    <body>
        <div id='printoutPanel'></div>
        
        <div id='myMap' style='width: 100vw; height: 100vh;'></div>
        <script type='text/javascript'>
            function loadMapScenario() {
                var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
                    credentials: 'My Bing Map Key - I removed here'
                });
                var pushpin = new Microsoft.Maps.Pushpin(map.getCenter(), null);
                var layer = new Microsoft.Maps.Layer();
                layer.add(pushpin);
                map.layers.insert(layer);
                
            }
        </script>
        <script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?branch=experimental&callback=loadMapScenario' async defer></script>
    </body>
</html>

Her is the file I created as map.component.html.

<div class='panel panel-primary'>
    <div class='panel-heading'>
        {{pageTitle}}
    </div>
     <div id='myMap' style='width: 100vw; height: 100vh;'></div> 
</div>

Here is the file I created as map.component.ts.

import { Component, OnInit } from 'angular2/core';

@Component({
    selector: 'pm-map',
    templateUrl: 'app/bingmap/map.component.html'
})

export class MapComponent implements OnInit {
    public pageTitle: string = "Map";
        
    var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
        credentials: 'Bing Map Key - I removed it here'
    });
    var pushpin = new Microsoft.Maps.Pushpin(map.getCenter(), null);
    var layer = new Microsoft.Maps.Layer();
    layer.add(pushpin);
    map.layers.insert(layer);
}
cocoseis
  • 1,234
  • 11
  • 33
tonyjy
  • 1,299
  • 3
  • 12
  • 24
  • What about Angular 2 is stopping you from including a Bing map? – Mike Cluck May 31 '16 at 15:50
  • How in the world did you get that impression? I'm asking why you can't just use a Bing map as is with Angular 2. What is it about Angular 2 that is currently making it difficult for you to have a Bing map on your site? – Mike Cluck May 31 '16 at 15:57
  • This is more AnguarJS 2 question. How do I wrap Bing Map as a AngularJS 2 component? Where can I add the function loadMapScenario and find the reference for Microsoft.Maps.Map? – tonyjy May 31 '16 at 16:03
  • @MikeC I have edited the question to make the question more explicit. The component couldn't be loaded on the page. – tonyjy May 31 '16 at 16:13

3 Answers3

14

I initially tried the accepted answer and ran into the issue some people had in the comments where upon load 'prototype' was null.

I initially solved this by using a try/catch with a setTimeout in the catch that would attempt to load bing after a second. This worked but was a very sloppy solution.

Eventually I looked for how people loaded Google Maps in angular to see if there was a better solution. Thankfully there is and it uses promises.

The solution that worked for me was found here in this answer. Full credit for this goes to them.

First create a service to load your map...

map-loader-service.service

import { Injectable } from '@angular/core';

const url = 'http://www.bing.com/api/maps/mapcontrol?callback=__onBingLoaded&branch=release';

@Injectable()
export class BingMapsLoader {
    private static promise;

    public static load() {
        // First time 'load' is called?
        if (!BingMapsLoader.promise) {

            // Make promise to load
            BingMapsLoader.promise = new Promise( resolve => {

                // Set callback for when bing maps is loaded.
                window['__onBingLoaded'] = (ev) => {
                    resolve('Bing Maps API loaded');
                };

                const node = document.createElement('script');
                node.src = url;
                node.type = 'text/javascript';
                node.async = true;
                node.defer = true;
                document.getElementsByTagName('head')[0].appendChild(node);
            });
        }

        // Always return promise. When 'load' is called many times, the promise is already resolved.
        return BingMapsLoader.promise;
    }
}

In the parent component to the component that contains the bing map element have this code...

import { BingMapsLoader } from './services/map-loader-service/map-loader-service.service';


export class BingMapParentComponent {
    mapReady = false;

    constructor() {
        BingMapsLoader.load()
            .then(res => {
                console.log('BingMapsLoader.load.then', res);
                this.mapReady = true;
        });
    }
}

Additionally, in the template of parent component have this code which will prevent the bing maps component from being initialized until it is ready.

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

Now, in the bing-map.component itself we want to wait until the component is in the DOM before loading the map.

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

And finally, you load the bing map in the bing-map.component

loadMap() {
    this.map = new Microsoft.Maps.Map(document.getElementById('mapId'), {
        credentials: 'Your Bing Maps Key Here',
    });
}
Dylan
  • 143
  • 1
  • 4
8

Your code is almost ok, you just need few modifications

1- in index.html remove the callback function and the div

<div id='myMap' style='width: 100vw; height: 100vh;'></div>
<script type='text/javascript'>
    function loadMapScenario() {
        var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
                credentials: 'My Bing Map Key - I removed here'
        });
        var pushpin = new Microsoft.Maps.Pushpin(map.getCenter(), null);
        var layer = new Microsoft.Maps.Layer();
        layer.add(pushpin);
        map.layers.insert(layer);
    }
</script>

Also, in index.html, remove the callback parameter from the script import.

<script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?branch=experimental&callback=loadMapScenario' async defer></script>

To be:

<script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?branch=experimental' async defer></script>

Now, you have the script loaded, all you need to do is create the map in your component

@Component({
    selector: 'pm-map',
    template: `
    <div class='panel panel-primary'>
      <div class='panel-heading'>
          {{pageTitle}}
      </div>
      <div #myMap style='width: 100%; height: 500px;'></div> 
    </div>`
})
export class MapComponent implements OnInit {
  @ViewChild('myMap') myMap; // using ViewChild to reference the div instead of setting an id
  public pageTitle: string = "Map";

  ngAfterViewInit(){  // after the view completes initializaion, create the map
    var map = new Microsoft.Maps.Map(this.myMap.nativeElement, {
        credentials: 'Bing Map Key - I removed it here'
    });
    var pushpin = new Microsoft.Maps.Pushpin(map.getCenter(), null);
    var layer = new Microsoft.Maps.Layer();
    layer.add(pushpin);
    map.layers.insert(layer);
  }
}

check it in this plunk

Abdulrahman Alsoghayer
  • 15,570
  • 7
  • 48
  • 55
  • @tonyjy you need to import it `import {ViewChild} from '@angular/core'` – Abdulrahman Alsoghayer Jun 01 '16 at 12:44
  • 2
    This gives me an `error TS2663: Cannot find name 'Microsoft'.` – cocoseis Aug 28 '16 at 18:57
  • @cocoseis, try installing the bingmaps typings by running: `typings install dt~bingmaps/microsoft.maps --global --save` – Abdulrahman Alsoghayer Aug 28 '16 at 19:15
  • @Abdulrahman: this throws `error TS2339: Property 'Layer' does not exist on type 'typeof Maps'.` and `error TS2339: Property 'layers' does not exist on type 'Map'.` – cocoseis Aug 28 '16 at 19:47
  • @cocoseis, I apologize, I gave you the definitions for bingmaps v7, I found no DefinitelyTyped project for bingmaps V8 . I only found the correct definitions "V8" in [this Microsoft github repository](https://github.com/Microsoft/Bing-Maps-V8-TypeScript-Definitions), you can follow the install instructions in the repo, or download the definitions files manually. – Abdulrahman Alsoghayer Aug 29 '16 at 06:10
  • I used those definitions and still getting a run time error with Cannot find name Microsoft. – Ernesto Oct 20 '16 at 12:19
  • 2
    @Ernesto The definitions are for compile time only. If you are getting an error at runtime, then you probably haven't added `script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?branch=experimental' async defer>`, did you ? – Abdulrahman Alsoghayer Oct 20 '16 at 12:30
  • You are absolutely right. I'm getting a different error now tho. Is there a dev version of the library? Error in ./AppComponent class AppComponent_Host - inline template:0:0 caused by: Cannot read property 'prototype' of null – Ernesto Oct 20 '16 at 12:36
  • @Ernesto I quickly looked for a dev version in [bing api website](http://www.bing.com/api/maps/sdk/mapcontrol/isdk#overview). But I couldn't find anything. Although, the link I gave you in my previous comment is for an "experimental" release, I think it's equivalent to a dev release. That aside, could you reproduce your issue in a plunker so I can take a look at it ? – Abdulrahman Alsoghayer Oct 20 '16 at 12:46
  • My plunker is very similar to yours, actually based of of it, but using zonejs 0.6.26. Maybe some other library discrepancy as well. I think related to this https://social.msdn.microsoft.com/Forums/office/en-US/66e9522f-452b-4ee7-b2cb-11257ec72322/bing-map-v8-incompatible-with-zonejs-angular-2?forum=bingmapsajax – Ernesto Oct 20 '16 at 13:33
  • I am running into the below issue as well.... EXCEPTION: Uncaught (in promise): Error: Error in ./XXX class XXX - inline template:19:0 caused by: Cannot read property 'prototype' of null TypeError: Cannot read property 'prototype' of null at k (https: www.bing.com/mapspreview/sdkfrozen/mapcontrol?branch=frozen:11:7073) ......................................deleted stacktrace for brevity........................... us/polyfills.bundle.js:15476:35) at HTMLDocument.ZoneTask.invoke (https://localhost:3000/en-us/polyfills.bundle.js:15414:25) – dot net learner Nov 17 '16 at 05:57
  • In my case it works only if i add 'settimeout' function in my component to load map after 1000 ms. Even then I frequently get error 'Cannot read property 'prototype' of null'. Does anyone have the same problem ? – Milos Aug 28 '17 at 09:34
  • 1
    This should not be the accepted answer, given the many reports of script errors and setTimeout workarounds in the comments. By removing the callback param and including 'async defer' in the script tag, there's no reliable way to know when the Maps script is loaded and ready for use. The Promise+callback method in the answer below is much better. – drwestco May 10 '20 at 16:24
2

There is a community effort to build Angular2 directives for Bing Maps. Still in alpha version, but a basic demo can be found here: http://ng2-bingmaps.azurewebsites.net/

The Github repo is here: https://github.com/youjustgo/ng2-bingmaps

Boland
  • 1,340
  • 1
  • 11
  • 32