Skip to content

Angular Quickstart

Angular 17+ standalone components.

Install

bash
npm install maplibre-gl

Component

ts
// map.component.ts
import {
  Component, ElementRef, ViewChild,
  AfterViewInit, OnDestroy
} from '@angular/core';
import maplibregl, { Map } from 'maplibre-gl';

@Component({
  selector: 'app-map',
  standalone: true,
  template: `<div #mapEl class="map"></div>`,
  styles: [`.map { width: 100%; height: 100vh; }`]
})
export class MapComponent implements AfterViewInit, OnDestroy {
  @ViewChild('mapEl') el!: ElementRef<HTMLDivElement>;
  private map?: Map;

  ngAfterViewInit(): void {
    const key = import.meta.env['NG_APP_MFD_PUB_KEY'];
    this.map = new maplibregl.Map({
      container: this.el.nativeElement,
      style: `https://api.mapsfordevs.com/styles/standard.json?key=${key}`,
      center: [28.04, -26.20],
      zoom: 11
    });
  }

  ngOnDestroy(): void { this.map?.remove(); }
}
css
/* styles.css — global */
@import 'maplibre-gl/dist/maplibre-gl.css';

Service

ts
// services/mapsfordevs.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';

export interface GeocodeItem {
  lat: number; lng: number; label: string;
  confidence: number; type: string;
}
interface Resp<T> { ok: boolean; items?: T[]; error?: string; }

@Injectable({ providedIn: 'root' })
export class MapsForDevsService {
  private base = 'https://api.mapsfordevs.com';
  private key  = import.meta.env['NG_APP_MFD_PUB_KEY'];

  constructor(private http: HttpClient) {}

  async geocode(query: string, country?: string, limit = 5): Promise<GeocodeItem[]> {
    const params: Record<string, string> = { q: query, limit: String(limit) };
    if (country) params['country'] = country;

    const body = await firstValueFrom(
      this.http.get<Resp<GeocodeItem>>(`${this.base}/geocode/forward`, {
        params,
        headers: { Authorization: `Bearer ${this.key}` }
      })
    );
    if (!body.ok) throw new Error(body.error);
    return body.items ?? [];
  }
}

Register HttpClient in app.config.ts:

ts
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
  providers: [provideHttpClient()]
};

Signal-based language switcher

ts
import { Component, signal, effect, inject } from '@angular/core';
import { MapStateService } from './map-state.service';

@Component({
  selector: 'app-lang-picker',
  standalone: true,
  template: `
    <select [value]="lang()" (change)="lang.set($any($event.target).value)">
      <option value="en">English</option>
      <option value="de">Deutsch</option>
      <option value="ja">日本語</option>
    </select>
  `
})
export class LangPickerComponent {
  private mapState = inject(MapStateService);
  lang = signal('en');

  constructor() {
    effect(() => {
      const map = this.mapState.map();
      const l = this.lang();
      if (!map || !map.isStyleLoaded()) return;
      ['place_label', 'transportation_name', 'poi_label'].forEach(id =>
        map.setLayoutProperty(id, 'text-field', [
          'coalesce', ['get', `name:${l}`], ['get', 'name:latin'], ['get', 'name']
        ])
      );
    });
  }
}