Appearance
Embed with Angular
Use the Mapsted Maps JavaScript API inside an Angular 17+ application. This tutorial covers the @ViewChild + AfterViewInit + OnDestroy lifecycle pattern, standalone components (Angular 17 default), NgModule-based projects, and Angular Universal SSR safety via isPlatformBrowser.
Prerequisites
- Node.js 20 or later
- npm 9 or later
- Angular 17 or later (standalone components recommended)
- Basic familiarity with Angular components and dependency injection
1. Install the API
bash
npm install @mapsted/maps-js-apiLicensed customers
The @mapsted/maps-js-api package is currently access-restricted on npm. If npm install returns 404 or 401, contact your Mapsted account manager for an npm token, then run npm config set //registry.npmjs.org/:_authToken <token> before re-running install.
2. Angular component pattern
Create a standalone component that implements AfterViewInit and OnDestroy. Use @ViewChild to obtain a reference to the map container element, initialize the map in ngAfterViewInit, and clean up in ngOnDestroy.
typescript
// src/app/components/mapsted-map/mapsted-map.component.ts
import {
Component,
Input,
ViewChild,
ElementRef,
AfterViewInit,
OnDestroy,
} from '@angular/core';
import { init } from '@mapsted/maps-js-api';
import type { MapInstance } from '@mapsted/maps-js-api';
@Component({
selector: 'app-mapsted-map',
standalone: true,
template: `
<div #mapContainer style="width: 100%; height: 100vh;"></div>
`,
})
export class MapstedMapComponent implements AfterViewInit, OnDestroy {
@Input() propertyId!: number;
@ViewChild('mapContainer') mapContainer!: ElementRef<HTMLDivElement>;
private mapInstance: MapInstance | null = null;
async ngAfterViewInit(): Promise<void> {
this.mapInstance = await init({
element: this.mapContainer.nativeElement,
propertyId: this.propertyId,
onload: () => {
console.log('Mapsted map ready');
},
});
}
async ngOnDestroy(): Promise<void> {
await this.mapInstance?.destroy();
this.mapInstance = null;
}
}Use it from a parent component or directly in app.component.ts:
typescript
// src/app/app.component.ts
import { Component } from '@angular/core';
import { MapstedMapComponent } from './components/mapsted-map/mapsted-map.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [MapstedMapComponent],
template: `
<app-mapsted-map [propertyId]="1643" />
`,
})
export class AppComponent {}3. Angular Universal (SSR)
If your project uses Angular Universal (server-side rendering), the API must only initialize in the browser. It accesses window, document, and creates an <iframe> — all browser-only APIs that throw during server rendering.
Inject PLATFORM_ID and use isPlatformBrowser() to guard the init() call:
typescript
// src/app/components/mapsted-map/mapsted-map.component.ts
import {
Component,
Input,
ViewChild,
ElementRef,
AfterViewInit,
OnDestroy,
Inject,
PLATFORM_ID,
} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { init } from '@mapsted/maps-js-api';
import type { MapInstance } from '@mapsted/maps-js-api';
@Component({
selector: 'app-mapsted-map',
standalone: true,
template: `
<div #mapContainer style="width: 100%; height: 100vh;"></div>
`,
})
export class MapstedMapComponent implements AfterViewInit, OnDestroy {
@Input() propertyId!: number;
@ViewChild('mapContainer') mapContainer!: ElementRef<HTMLDivElement>;
private mapInstance: MapInstance | null = null;
constructor(@Inject(PLATFORM_ID) private platformId: object) {}
async ngAfterViewInit(): Promise<void> {
// Guard: only initialize in the browser, not during SSR
if (!isPlatformBrowser(this.platformId)) return;
this.mapInstance = await init({
element: this.mapContainer.nativeElement,
propertyId: this.propertyId,
});
}
async ngOnDestroy(): Promise<void> {
await this.mapInstance?.destroy();
this.mapInstance = null;
}
}isPlatformBrowser(this.platformId) returns false on the server and true in the browser. The early return ensures the component renders an empty container during SSR and initializes the map only after hydration.
4. Standalone components vs NgModules
Angular 17 projects use standalone components by default — the examples above all use standalone: true and import dependencies directly in the imports array.
For older projects that use NgModules, import the standalone component into the module via the imports array — do not place it in declarations. Standalone components (standalone: true) cannot be declared; putting one in declarations causes a hard NG0994 compile error in Angular 14+.
Standalone components go in imports, not declarations
Angular 14+ enforces that a component decorated with standalone: true must be added to an NgModule's imports array, not its declarations array. Using declarations will produce a compile-time NG0994 error.
If your project is not using standalone components (pre-Angular 17 or opted-out), create a classic component without standalone: true and add it to the module's declarations array instead:
typescript
// src/app/map/mapsted-map.component.ts (NgModule-compatible, no standalone: true)
import {
Component,
Input,
ViewChild,
ElementRef,
AfterViewInit,
OnDestroy,
} from '@angular/core';
import { init } from '@mapsted/maps-js-api';
import type { MapInstance } from '@mapsted/maps-js-api';
@Component({
selector: 'app-mapsted-map',
// NOTE: no standalone: true — this component is declared in MapModule below
template: `
<div #mapContainer style="width: 100%; height: 100vh;"></div>
`,
})
export class MapstedMapComponent implements AfterViewInit, OnDestroy {
@Input() propertyId!: number;
@ViewChild('mapContainer') mapContainer!: ElementRef<HTMLDivElement>;
private mapInstance: MapInstance | null = null;
async ngAfterViewInit(): Promise<void> {
this.mapInstance = await init({
element: this.mapContainer.nativeElement,
propertyId: this.propertyId,
});
}
async ngOnDestroy(): Promise<void> {
await this.mapInstance?.destroy();
this.mapInstance = null;
}
}Then wire it into MapModule:
typescript
// src/app/map/map.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MapstedMapComponent } from './mapsted-map.component';
@NgModule({
declarations: [MapstedMapComponent], // non-standalone: goes in declarations
imports: [CommonModule],
exports: [MapstedMapComponent],
})
export class MapModule {}Then import MapModule in AppModule (or any feature module) and use <app-mapsted-map> in templates:
typescript
// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MapModule } from './map/map.module';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, MapModule],
bootstrap: [AppComponent],
})
export class AppModule {}Migrating to standalone?
Angular 17 ships with ng generate component --standalone as the default. If you are starting a new project, standalone components are recommended — they have fewer boilerplate files and work directly with the inject() function in Angular 14+.
5. Live sandbox preview
The sandbox below runs the same map using the CDN-global API, showing what your component will render:
6. Angular service recipe
For an Angular service that wraps the init / destroy lifecycle and makes the MapInstance injectable throughout your application, see the companion guide: Angular MapstedMapService recipe
Next steps
- Subscribe to map events: Map events
- Add markers and popups: Markers and popups
- Prefer React instead? See Embed with React and Next.js
- Prefer Vue? See Embed with Vue 3 and Nuxt