Unleashing Location Visualization: Implementing Leaflet JavaScript Library to Display Interactive Maps (Part 5) in Angular-15 Ionic-7 App

Welcome to part 5 of my series on building my first Angular-15 Ionic-7 app. In this post, I will show you how to use the Leaflet JavaScript library to display a single location map on your Ionic app. The leaflet is a popular open-source library for interactive maps that work well with Ionic and Angular. You will learn how to install and configure Leaflet, how to create a map component, and how to add a marker and a popup to the map. By the end of this post, you will have a basic understanding of how to use Leaflet in your Ionic app and how to customize it to your needs.


Since individual modules are provided with latitude and longitude values with the location name of the module, I will first create a bottom sheet style pop-up for ever module.

The tab1-details.page.html page currently displays only the static values of the location. I will add a button that will open a map modal for every module page like this:

...
<ion-content [fullscreen]="true">
 ...
  <ion-card *ngIf="module">
    ...
  </ion-card>

  <ion-card *ngIf="module">
    <ion-card-header>
      <ion-card-title>Location</ion-card-title>
      <ion-card-subtitle>{{module.moduleName}}</ion-card-subtitle>
    </ion-card-header>
    <ion-card-content>
     ...
        <ion-item>
          <ion-label>Latitude: {{module.lat}}</ion-label>
        </ion-item>
        <ion-item>
          <ion-label>Longitude: {{module.long}}</ion-label>
        </ion-item>

        <ion-button
          (click)="openMap(module)"
          expand="full"
          fill="solid"
          shape="round"
        >
          View on Map
        </ion-button>
      </ion-list>
    </ion-card-content>
  </ion-card>
</ion-content>

The click event listener will call the method openMap(module) which will take in the module object.

Next, I will create a new page that will behave as the modal page view for the map using the command:

ionic generate page pages/map

Then I transfer the map’s route from app.route.ts to tabs.route.ts with this:

{
        path: 'map',
        loadComponent: () => import('../map/map.page').then((m) => m.MapPage),
      },

It is also important to import the map page in the app.component.ts file and put it into the imports array there to make the modal appear on top of the other pages.

Now, I will create the logic for this method in the tab1-details.page.ts page in the following way:

...
import { ActivatedRoute, RouterLink } from '@angular/router';
import {
  ModuleServiceService,
  Module,
} from 'src/app/services/module-service.service';
import { ModalController } from '@ionic/angular';
import { MapPage } from '../map/map.page';

@Component({
  selector: 'app-tab1-details',
  templateUrl: './tab1-details.page.html',
  styleUrls: ['./tab1-details.page.scss'],
  standalone: true,
  imports: [IonicModule, CommonModule, FormsModule, RouterLink, MapPage],
})
export class Tab1DetailsPage implements OnInit {
  ...
  constructor(
    private activatedRoute: ActivatedRoute, // used to get the moduleNo from the url
    private moduleService: ModuleServiceService,
    private modalCtrl: ModalController
  ) {}

  ngOnInit() {
   ...
  }
  ...
  // create a method to open the map
  async openMap(module: any) {
    //open the map in a modal
    const modal = await this.modalCtrl.create({
      // create a modal
      component: MapPage, // set the modal component
      componentProps: {
        moduleId: module.moduleNo,
        moduleLat: module.lat,
        moduleLong: module.long,
        moduleLocation: module.location,
        moduleRoom: module.room,
      }, // set the modal component properties
      breakpoints: [0, 0.75, 1], // set the breakpoints
      initialBreakpoint: 0.75, // set the initial breakpoint
    }); // create a modal
    modal.present(); // present the modal
  }
}

The above code creates an asynchronous method called openMap that takes in a parameter called module. Within the openMap method, a modal is created using the create method of the modalCtrl object. The component property of the modal is set to MapPage, which is presumably a page/component that displays a map. The componentProps property is an object that contains properties of the module parameter. These properties include the moduleNo, lat, long, location, and room of the module object. These properties are then passed to the MapPage component. The breakpoints property is an array that specifies the breakpoints for the modal. The initialBreakpoint property specifies the initial breakpoint for the modal. Once the modal is created, the present method is called to present the modal to the user.

To begin working with maps, I tried out multiple providers, and found that Leaflet JS library provides a quick setup at no cost. I referenced the following for this:

Leaflet — an open-source JavaScript library for interactive maps

I entered the following command to install the leaflet library:

npm install leaflet

The leaflet library will appear in the package.json file along with other dependencies. Next, I begin to write the logic to access the map, markers and tile layers from the leaflet documentation in the map.page.ts file in the following way:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { Input } from '@angular/core';
import * as L from 'leaflet';
import { Module } from 'src/app/services/module-service.service';

@Component({
  selector: 'app-map',
  templateUrl: './map.page.html',
  styleUrls: ['./map.page.scss'],
  standalone: true,
  imports: [IonicModule, CommonModule, FormsModule],
})
export class MapPage implements OnInit {
  module!: Module; // module object
  @Input() moduleId!: string; // id of the module taken from the modal component props
  @Input() moduleLat!: number; // latitude of the module taken from the modal component props
  @Input() moduleLong!: number; // longitude of the module taken from the modal component props
  @Input() moduleLocation!: string; // location of the module taken from the modal component props
  @Input() moduleRoom!: string; // room of the module taken from the modal component props

  map!: L.Map;

  constructor() {}

  ngOnInit() {}

  ionViewDidEnter() {
    // this is a lifecycle hook that is called when the view is loaded into the DOM and ready to be presented to the user
    // create a spinner to show while the map is loading
    const spinner = document.createElement('ion-spinner');
    spinner.name = 'crescent';
    spinner.style.cssText = 'position: absolute; top: 10%; left: 50%;';
    document.getElementById('map')?.appendChild(spinner);

    setTimeout(() => {
      // timeout to wait for the modal to load and for the div id called map to be created in the html otherwise the the error appears that 'el' property is undefined

      this.map = L.map('map').setView([this.moduleLat, this.moduleLong], 16);
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution:
          'Map data © <a href="<https://www.openstreetmap.org/>">OpenStreetMap</a> contributors',
      }).addTo(this.map);

      const blueIcon = L.icon({
        iconUrl: 'assets/images/marker-icon-2x.png', //remember to shift the images folder from the node_modules/leaflet/dist/images to the src/assets/images folder otherwise the images will not be found.
        iconSize: [25, 41],
        iconAnchor: [12, 41],
        popupAnchor: [1, -34],
        tooltipAnchor: [16, -28],
        shadowUrl: 'assets/images/marker-shadow.png',
        shadowSize: [41, 41],
        shadowAnchor: [12, 41],
      });

      // add a marker to the map
      L.marker([this.moduleLat, this.moduleLong], { icon: blueIcon })
        .addTo(this.map)
        .bindPopup(this.moduleLocation + ' | Room No. ' + this.moduleRoom)
        .openPopup();

      // add a circle to the map
      L.circle([this.moduleLat, this.moduleLong], {
        color: 'red',
        fillColor: '#f03',
        fillOpacity: 0.5,
        radius: 50,
      }).addTo(this.map);
    }, 500);
  }
}

The above code defines a MapPage class that implements the OnInit interface. The class contains several input properties (moduleId, moduleLat, moduleLong, moduleLocation, and moduleRoom) that are used to display a map. In the ionViewDidEnter lifecycle hook, a spinner is created and added to the DOM. After a timeout of 500 milliseconds, the map is initialized using the Leaflet.js library and displayed on the page. The setView method sets the initial map view to the latitude and longitude specified in the input properties. A tile layer from OpenStreetMap is added to the map using the tileLayer method. A blue marker is added to the map using the marker method, which represents the location of the module. A circle is also added to the map using the circle method with a red border, a fill color of #f03, and a radius of 50. Finally, the location and room of the module are displayed in a popup that is bound to the marker using the bindPopup method, and the popup is opened using the openPopup method.

Finally, the logic has to be implemented on the template file of map.page.html in the following way:

<ion-content [fullscreen]="true">
  <div class="map-container">
    <div class="map-frame">
      <div class="map" id="map"></div>
    </div>
  </div>
</ion-content>

Along with the above template, I have added some styling to the modal page for a better UI in the map.page.scss file like this:

.map-container {
  height: 100%;
  width: 100%;
}

.map-frame {
  height: 100%;
}

#map {
  height: 100%;
  margin-top: 20px;
}

I have referred to the following documentation to get this library up and running: Quick Start Guide - Leaflet - a JavaScript library for interactive maps (leafletjs.com). In Figure 16, the module detail page shows the button to open the map, and in Figure 17, the modal appears as a bottom sheet after clicking on the ‘View on Map’ button:

Figure 17: Map button on individual module details page to open map modal



Figure 18: Map modal appears with marker, circle, popup label and zoom buttons.


In this post, we have learned how to use the Leaflet JavaScript library to display a single location map in our Angular-15 Ionic-7 app. We have seen how to install and import the necessary dependencies, how to create a map component and a map service, how to initialize and configure the map object, and how to add a marker and a popup to the map. We have covered some of the basic features of Leaflet, but there are many more options and functionalities that you can explore on the official documentation. I hope you have enjoyed this series of posts on building your first Angular-15 Ionic-7 app and that you have gained some useful skills and insights along the way. Thank you for reading and happy coding!

Popular Posts

Perform CRUD (Create, Read, Update, Delete) Operations using PHP, MySQL and MAMP : Part 4. My First Angular-15 Ionic-7 App

Visualize Your Data: Showcasing Interactive Charts for Numerical Data using Charts JS Library (Part 23) in Your Angular-15 Ionic-7 App

How to Build a Unit Converter for Your Baking Needs with HTML, CSS & Vanilla JavaScript