Guidesv3.2 API Referencev3.1 API Reference
Guides

Build HERE Maps with Angular

 HERE Maps API for JavaScript version 3.2

Angular provides a set of powerful tools and features for building web applications. You can create an even more engaging experience for your users by combining these features with HERE SDKs and APIs.

For example, you can integrate HERE Maps with Angular to display interactive maps, geocode addresses, calculate routes, and more, all within the context of your Angular application.

See the following sections for step-by-step instructions on how to create a simple interactive map within the context of an Angular application.

Before you begin

Sign up for a HERE developer account and obtain your API credentials. For more information, see Get started.

Setup an Angular project

Facilitate the development of a new Angular application by using the Angular CLI.

  1. In the Command Prompt, enter the following command to install Angular CLI:

    npm install -g @angular/cli
  2. Initiate a new Angular project by entering the following command:

    ng new jsapi-angular && cd jsapi-angular
  3. At the Which stylesheet format would you like to use? prompt, select the CSS option.

  4. At the Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? prompt, press the N key, to disable server-side rendering and static site generation prerendering.

  5. At the Do you want to create a 'zoneless' application without zone.js? prompt, press the y key, to not include zone.js.

  6. At the Which AI tools do you want to configure with Angular best practices? prompt, press the N key, to not configure AI tools.

Result: The system installs the required packages and creates a new jsapi-angular directory, with the Angular components residing in the src sub-directory. The jsapi-angular directory has the following structure:

jsapi-angular/
├── README.md
├── .editorconfig
├── .gitignore
├── angular.json
├── package-lock.json
├── package.json
├── public
│   └── favicon.ico
├── src
│   ├── app
│   │   ├── app.config.ts
│   │   ├── app.css
│   │   ├── app.html
│   │   ├── app.routes.ts
│   │   ├── app.spec.ts
│   │   └── app.ts
│   ├── index.html
│   ├── main.ts
│   └── styles.css
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
  1. In your project directory, install the maps-api-for-javascript NPM package which is hosted at https://repo.platform.here.com/ by adding a registry entry to the NPM configuration through the following command:

    npm config set @here:registry https://repo.platform.here.com/artifactory/api/npm/maps-api-for-javascript/
  2. Install the package from the @here namespace by entering the following command:

    npm install @here/maps-api-for-javascript
  3. Navigate to the jsapi-angular folder, and then open the tsconfig.json file in a preferred text editor.

  4. In the tsconfig.json file, add the following setting under angularCompilerOptions:

    "allowSyntheticDefaultImports": true

    In the tsconfig.json file, within the compilerOptions object, add the following option:

    "allowJs": true
  5. Optional: To verify that you completed the setup successfully, perform the following actions:

    1. Enter the following command:

      ng serve

      Result: This command launches a development server with the "hot reload" functionality.

    2. Initiate the Angular application by navigating to the http://localhost:4200/ address in the browser.

      Result: The browser displays the Angular welcome screen with the Congratulations! Your app is running. message.

📘

To terminate the ng serve process for Windows operating systems, press q + Enter. If that doesn't work, open a Windows command shell, determine the process ID with netstat -ano | findstr 4200 and kill the process with taskkill /PID <process-ID> /F.

Add a static map component

In your Angular application, generate a static map by creating a component that contains an H.Map namespace instance. This component renders the map through the corresponding container.

  1. In the Angular CLI, generate the jsmap component by entering the following command:

    ng generate component jsmap

    Result: The command creates a jsmap folder in the src/app directory. The folder contains all the files required to build the component:

    └── src
        ├── app
        │   ├── app.config.ts
        │   ├── app.css
        │   ├── app.html
        │   ├── app.routes.ts
        │   ├── app.spec.ts
        │   ├── app.ts
        │   └── jsmap
        │       ├── jsmap.css
        │       ├── jsmap.html
        │       ├── jsmap.spec.ts
        │       └── jsmap.ts
        ...
  2. Navigate to the jsapi-angular/src/app/jsmap directory, and then open the jsmap.ts file.

  3. In the jsmap.ts file, replace the default code with the following code:

    import { Component, ViewChild, ElementRef } from '@angular/core';
    import '@here/maps-api-for-javascript';
    
    @Component({
      selector: 'app-jsmap',
      imports: [],
      templateUrl: './jsmap.html',
      styleUrl: './jsmap.css'
    })
    export class Jsmap {
    
      private map?: H.Map;
    
      @ViewChild('map') mapDiv?: ElementRef;
    
      ngAfterViewInit(): void {
        if (!this.map && this.mapDiv) {
          // Instantiate a platform, default layers and a map as usual.
          const platform = new H.service.Platform({
            apikey: '{YOUR_API_KEY}'
          });
          const layers = platform.createDefaultLayers();
          const map = new H.Map(
            this.mapDiv.nativeElement,
            // Add type assertion to the layers object...
            // ...to avoid any Type errors during compilation.
            (layers as any).vector.normal.map,
            {
              pixelRatio: window.devicePixelRatio,
              center: {lat: 52.5, lng: 13.4},
              zoom: 13,
            },
          );
          this.map = map;
        }
      }
    }

    This code imports the HERE Maps API for JavaScript library and instantiates the map through the ngAfterViewInit hook.

    📘

    Note

    Replace {YOUR_API_KEY} with a valid HERE API key.

  4. Open the jsmap.html file, and then replace the file contents with the following code:

    <div #map id="map"></div>
  5. Open the jsmap.css file, and then add the following style:

    #map {
      width: 100%;
      height: 500px;
    }
  6. In the jsapi-angular/src/app directory, open the app.html file, and then replace the file content with following code:

    <app-jsmap></app-jsmap>
  7. In the same directory, open the app.ts file and add the following elements:

    • Add the Jsmap import statement:

      import { Jsmap } from './jsmap/jsmap';
    • In the imports array, replace RouterOutlet with Jsmap:

      imports: [Jsmap],

    See the following updated app.ts file for reference:

    import { Component, signal } from '@angular/core';
    import { Jsmap } from './jsmap/jsmap';
    @Component({
      selector: 'app-root',
      imports: [Jsmap],
      templateUrl: './app.html',
      styleUrl: './app.css'
    })
    export class App {
      protected readonly title = signal('jsapi-angular');
    }
  8. Verify that the map renders correctly by navigating to the http://localhost:4200/ address in the browser.

Result: The browser renders a static map centered on Berlin, at zoom level 13, in the viewport that is 500 pixels high and takes 100% of the enclosing container's width, similar to the following example:

Basic static map component rendered with the HERE Maps API for JavaScript

Resize the map

A static map with a predetermined size cannot be changed during runtime.

You can improve a static map by adding dynamic resizing to make the map canvas react and adapt to the changes in the viewport size, for example, when the user expands the browser window.

To achieve that, the map needs an explicit resize() method call to adjust to the new dimensions of the container. This example uses simple-element-resize-detector to demonstrate how you can resize a map within an Angular component.

  1. In Angular CLI, ensure that your working directory is the jsapi-angular directory.

  2. Enter the following commands :

    npm install simple-element-resize-detector --save
    npm install @types/simple-element-resize-detector --save-dev
  3. From the src/app/jsmap directory, open the jsmap.ts file, and then adjust the import statements by adding the simple-element-resize-detector library, as shown in the following example:

    import { Component, ViewChild, ElementRef } from '@angular/core';
    import '@here/maps-api-for-javascript';
    import onResize from 'simple-element-resize-detector'; // New import statement
  4. Update ngAfterViewInit method with the map.getViewPort().resize() method call, as shown in the following code snippet:

      ngAfterViewInit(): void {
        if (!this.map && this.mapDiv) {
          const platform = new H.service.Platform({
            apikey: '{YOUR_API_KEY}'
          });
          const layers = platform.createDefaultLayers();
          const map = new H.Map(
            this.mapDiv.nativeElement,
            (layers as any).vector.normal.map,
            {
              pixelRatio: window.devicePixelRatio,
              center: {lat: 52.5, lng: 13.4},
              zoom: 13,
            },
          );
          onResize(this.mapDiv.nativeElement, () => {
            map.getViewPort().resize();
          }); // Sets up the event listener to handle resizing
          this.map = map;
        }
      }

Result: The component adjusts dynamically to the changes in the size of the enclosing browser window.

Set the map to respond to input parameters

Use another component to take your input to change the zoom level and the center of the map.

  1. Create a new component by entering the following command in Angular CLI:

    ng generate component mapposition
  2. Navigate to the src/app/mapposition directory, and then open the mapposition.html file.

  3. Replace the default content of the mapposition.html file with the following code:

    <div class="input-group">
      <label for="zoom">Zoom:</label>
      <input
        id="zoom"
        (change)="notify.emit($event)"
        name="zoom"
        type="number"
        value="13"
      />
    </div>
    <div class="input-group">
      <label for="lat">Latitude:</label>
      <input
        id="lat"
        (change)="notify.emit($event)"
        name="lat"
        type="number"
        value="52.5"
      />
    </div>
    <div class="input-group">
      <label for="lng">Longitude:</label>
      <input
        id="lng"
        (change)="notify.emit($event)"
        name="lng"
        type="number"
        value="13.4"
      />
    </div>
    📘

    Note

    The mapposition component has three input fields: zoom, latitude and longitude. The new code introduces a change event listener that redispatches events for each input field to the parent component.

  4. From the src/app/mapposition/ directory, open the mapposition.css file, and then replace the default contents with the following basic style configuration:

    .input-group {
      margin-bottom: 5px;
      margin-top: 5px;
      display: flex;
      align-items: center;
    }
    
    label {
      width: 100px;
      font-weight: bold;
    }
    
    input[type="number"] {
      width: 100px;
      padding: 5px;
      margin-left: 10px;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
    
    input[type="number"]:focus {
      border-color: #007bff;
      outline: none;
    }
  5. From the src/app/mapposition/ directory, open the mapposition.ts file, and then replace the file content with the following code:

    import { Component, Output, EventEmitter } from '@angular/core';
    
    @Component({
      selector: 'app-mapposition',
      imports: [],
      templateUrl: './mapposition.html',
      styleUrl: './mapposition.css'
    })
    export class Mapposition {
      @Output() notify = new EventEmitter();
    }
    📘

    Note

    This TypeScript part of the component uses the @Output decorator and the EventEmitter class to notify the parent component about the changes in the user input.

  6. From the src/app directory, open the app.html file, and then replace the file contents with the following code:

    <app-jsmap
      [zoom]="zoom"
      [lat]="lat"
      [lng]="lng"
    ></app-jsmap>
    <app-mapposition
      (notify)="handleInputChange($event)"
    ></app-mapposition>
    📘

    Note

    This code uses the parent app component to pass the values between the map and input fields.

  7. From the src/app directory, open the app.ts file, and then add a change handler by replacing the file content with the following code:

    import { Component } from '@angular/core';
    import { Jsmap } from './jsmap/jsmap';
    import { Mapposition } from './mapposition/mapposition'; // Imports Mapposition
    
    @Component({
      selector: 'app-root',
      imports: [Jsmap, Mapposition], // Adds Mapposition to the App template
      templateUrl: './app.html',
      styleUrl: './app.css'
    })
    export class App {
      title = 'jsapi-angular';
    
      // Initialized by the constructor, these properties store the current zoom level and coordinates (latitude and longitude) for the map
      constructor() {
        this.zoom = 13;
        this.lat = 52.5;
        this.lng = 13.4;
      }
    
      zoom: number;
      lat: number;
      lng: number;
    
      // Updates the zoom, lat, and lng properties based on user input.
      handleInputChange(event: Event) {
        const target = <HTMLInputElement> event.target;
        if (target) {
          if (target.name === 'zoom') {
            this.zoom = parseFloat(target.value);
          }
          if (target.name === 'lat') {
            this.lat = parseFloat(target.value);
          }
          if (target.name === 'lng') {
            this.lng = parseFloat(target.value);
          }
        }
      }
    }

    Result: The change handler updates the values according to the user's input and Angular passes these values to the jsmap component.

  8. From the src/app/jsmap directory, open the jsmap.ts file, and then perform the following actions:

    1. Adjust the first import statement to include Input and SimpleChanges classes, as shown in the following example:
      import { Component, ViewChild, ElementRef, Input, SimpleChanges } from '@angular/core';
    2. Within the Jsmap class, after the existing ngAfterViewInit hook definition, add @Input decorators for zoom, latitude, and longitude, and then add the ngOnChanges hook definition, as shown in the following code snippet:
      @Input() public zoom = 13;
      @Input() public lat = 52.5;
      @Input() public lng = 13.4;
      
      ngOnChanges(changes: SimpleChanges) {
        if (this.map) {
          if (changes['zoom'] !== undefined) {
            this.map.setZoom(changes['zoom'].currentValue);
          }
          if (changes['lat'] !== undefined) {
            this.map.setCenter({lat: changes['lat'].currentValue, lng: this.lng});
          }
          if (changes['lng'] !== undefined) {
            this.map.setCenter({lat: this.lat, lng: changes['lng'].currentValue});
          }
        }
      }

Result: Now, your Angular application can take your input with the help of the mapposition component, store the state in the app component, and then update the jsmap component so that the map responds to the input.

Enable dragging and zooming

Further increase the interactivity of your map by enabling map manipulation in the form of dragging or zooming in or out of the view.

To achieve this behavior, add the mapviewchange listener to the H.Map instance, and then update the app state with the help of the EventEmitter.

  1. In the app.ts file, update the App state by adding the following code:

      handleMapChange(event: H.map.ChangeEvent) {
        if (event.newValue.lookAt) {
          const lookAt = event.newValue.lookAt;
          this.zoom = lookAt.zoom;
          this.lat = lookAt.position.lat;
          this.lng = lookAt.position.lng;
        }
      }
  2. In the app.html file, replace the current content with the following code:

    <app-jsmap
      [zoom]="zoom"
      [lat]="lat"
      [lng]="lng"
      (notify)="handleMapChange($event)"
    ></app-jsmap>
    <app-mapposition
      [zoom]="zoom"
      [lat]="lat"
      [lng]="lng"
      (notify)="handleInputChange($event)"
    ></app-mapposition>
  3. Update the jsmap.ts file by performing the following actions:

    1. In the jsmap.ts file, adjust the import statement by adding the Output and EventEmitter classes, as shown in the following example:

      import { Component, ViewChild, ElementRef, Input, SimpleChanges, Output, EventEmitter } from '@angular/core';
    2. Update the ngAfterViewInit hook by attaching the listener that enables the interactive behavior after the map instantiation:

      map.addEventListener('mapviewchange', (ev: H.map.ChangeEvent) => {
        this.notify.emit(ev)
      });
      new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
    3. Update the ngOnChanges method to throttle the unnecessary map updates:

        private timeoutHandle: any;
        @Output() notify = new EventEmitter();
      
        ngOnChanges(changes: SimpleChanges) {
            clearTimeout(this.timeoutHandle);
            this.timeoutHandle = setTimeout(() => {
              if (this.map) {
                if (changes['zoom'] !== undefined) {
                  this.map.setZoom(changes['zoom'].currentValue);
                }
                if (changes['lat'] !== undefined) {
                  this.map.setCenter({lat: changes['lat'].currentValue, lng: this.lng});
                }
                if (changes['lng'] !== undefined) {
                  this.map.setCenter({lat: this.lat, lng: changes['lng'].currentValue});
                }
              }
            }, 100);
        }
  4. In the mapposition.ts file, update the component to reflect the current position of the map:

    1. Import the Input decorator from the @angular/core, as shown in the following example:
      import { Component, Output, EventEmitter, Input } from '@angular/core';
    2. Add the following input parameters in the class body:
        @Input() public zoom = 13;
        @Input() public lat = 52.5;
        @Input() public lng = 13.4;
  5. In the mapposition.html file, adjust the template by replacing the file content with the following code:

    <div class="input-group">
      <label for="zoom">Zoom:</label>
      <input
        id="zoom"
        (change)="notify.emit($event)"
        name="zoom"
        type="number"
        [value]="zoom"
      />
    </div>
    <div class="input-group">
      <label for="lat">Latitude:</label>
      <input
        id="lat"
        (change)="notify.emit($event)"
        name="lat"
        type="number"
        [value]="lat"
      />
    </div>
    <div class="input-group">
      <label for="lng">Longitude:</label>
      <input
        id="lng"
        (change)="notify.emit($event)"
        name="lng"
        type="number"
        [value]="lng"
      />
    </div>

Result: The resulting application consist of the interactive map and input fields. When you interact with the map, the application automatically updates the input fields, as shown in the following example:

Interactive Map component

Code samples

The following sections contains the full code samples for Jsmap, App, and Mapposition, which are the main Angular components used in this tutorial.

Jsmap component

View the jsmap.ts source code.

import { Component, ViewChild, ElementRef, Input, SimpleChanges, Output, EventEmitter } from '@angular/core';
import '@here/maps-api-for-javascript';
import onResize from 'simple-element-resize-detector';

@Component({
    selector: 'app-jsmap',
    imports: [],
    templateUrl: './jsmap.html',
    styleUrl: './jsmap.css'
})
export class Jsmap {

  private map?: H.Map;

  @ViewChild('map') mapDiv?: ElementRef;

    ngAfterViewInit(): void {
    if (!this.map && this.mapDiv) {
      const platform = new H.service.Platform({
        apikey: 'YOUR_HERE_API_KEY'
      });
      const layers = platform.createDefaultLayers();
      const map = new H.Map(
        this.mapDiv.nativeElement,
        (layers as any).vector.normal.map,
        {
          pixelRatio: window.devicePixelRatio,
          center: {lat: 52.53074, lng: 13.38492},
          zoom: 19,
        },
      );
      onResize(this.mapDiv.nativeElement, () => {
        map.getViewPort().resize();
      });
      this.map = map;

      map.addEventListener('mapviewchange', (ev: H.map.ChangeEvent) => {
        this.notify.emit(ev)
      });
      new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
    }
  }

  @Input() public zoom = 13;
  @Input() public lat = 52.5;
  @Input() public lng = 13.4;

  private timeoutHandle: any;
  @Output() notify = new EventEmitter();

  ngOnChanges(changes: SimpleChanges) {
      clearTimeout(this.timeoutHandle);
      this.timeoutHandle = setTimeout(() => {
        if (this.map) {
          if (changes['zoom'] !== undefined) {
            this.map.setZoom(changes['zoom'].currentValue);
          }
          if (changes['lat'] !== undefined) {
            this.map.setCenter({lat: changes['lat'].currentValue, lng: this.lng});
          }
          if (changes['lng'] !== undefined) {
            this.map.setCenter({lat: this.lat, lng: changes['lng'].currentValue});
          }
        }
      }, 100);
  }
}
📘

Note

Replace YOUR_HERE_API_KEY in the previous code sample with a valid API key.

App component

View the app.ts source code.

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { JsmapComponent } from './jsmap/jsmap.component';
import { MappositionComponent } from './mapposition/mapposition.component';

@Component({
  selector: 'app-root',
  imports: [Jsmap, Mapposition], // Adds Mapposition to the App template
  templateUrl: './app.html',
  styleUrl: './app.css'
  })
export class App {
  title = 'jsapi-angular';

  constructor() {
    this.zoom = 13;
    this.lat = 52.5;
    this.lng = 13.4;
  }

  zoom: number;
  lat: number;
  lng: number;

  handleInputChange(event: Event) {
    const target = <HTMLInputElement> event.target;
    if (target) {
      if (target.name === 'zoom') {
        this.zoom = parseFloat(target.value);
      }
      if (target.name === 'lat') {
        this.lat = parseFloat(target.value);
      }
      if (target.name === 'lng') {
        this.lng = parseFloat(target.value);
      }
    }
  }

  handleMapChange(event: H.map.ChangeEvent) {
    if (event.newValue.lookAt) {
      const lookAt = event.newValue.lookAt;
      this.zoom = lookAt.zoom;
      this.lat = lookAt.position.lat;
      this.lng = lookAt.position.lng;
    }
  }
}

Mapposition component

View the mapposition.ts source code.

import { Component, Output, EventEmitter, Input } from '@angular/core';

@Component({
  selector: 'app-mapposition',
  imports: [],
  templateUrl: './mapposition.html',
  styleUrl: './mapposition.css'
})
export class Mapposition {

  @Output() notify = new EventEmitter();

  @Input() public zoom = 13;
  @Input() public lat = 52.5;
  @Input() public lng = 13.4;

}

Next steps

To further explore the design and features of the HERE Maps API for JavaScript, see the API Reference.