Appearance
v-map mit Angular
Dieser Guide zeigt, wie du v-map in eine Angular-App einbindest. Die Live-Demo unten ist die echte App aus examples/angular/ des v-map-Repos, gebaut mit Angular 19 (standalone components, signals, zoneless change detection) und in einem sandboxed Iframe eingebettet.
Live-Demo
Live demo (sandboxed Iframe): angular/ · in neuem Tab öffnen
Die Angular-Demo zeigt dieselben Features wie die anderen Framework-Showcases, nur mit Angular-19-Idiom:
- Reactive Provider-Switch:
signal<Provider>('ol')↔flavour-Attribut am<v-map>-Element via[attr.flavour]="provider()". Provider-Wechsel ohne Re-Mount. - Reactive Zoom-Slider:
signal<number>(11)mit(input)Event Handler, gebunden via[attr.zoom]="zoom().toString()". - Layer-Toggle: Angular's neuer
@if-Control-Flow blendet<v-map-layer-*>Komponenten ein/aus. <v-map-error>Toast: deklarativer Fehler-Stapel unten rechts, ohne JavaScript-Listener-Code.- Programmatischer
vmap-error-Listener: Angular's Template-Binding unterstützt Custom-Element-Events mit Bindestrich direkt:(vmap-error)="onMapError($event)". Wie in Vue 3 — keinuseRef/addEventListenerBoilerplate wie in React 18. CUSTOM_ELEMENTS_SCHEMA: Angular's Template-Compiler ist sehr strikt — ohne dieses Schema im Component verweigert er die v-map* Tags als „unknown elements".- Zoneless Change Detection: der Demo nutzt
provideExperimentalZonelessChangeDetection()(Angular 18+), so dass Signals die einzige Quelle der Wahrheit sind und kein zone.js Patching die Custom-Element-Events stört.
Setup für eigene Projekte
1. Angular-Projekt anlegen
bash
pnpm dlx @angular/cli@latest new my-vmap-app --standalone --style=css --routing=false
cd my-vmap-app
pnpm install2. v-map einbinden
Wie bei den anderen Frameworks lädst du v-map am robustesten direkt von jsDelivr per <script type="module"> im index.html, statt den Loader durch Angular's esbuild-Pipeline zu bündeln:
html
<!-- src/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My v-map App</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@npm9912/v-map@0.3.0/dist/v-map/v-map.esm.js"
></script>
</head>
<body>
<app-root></app-root>
</body>
</html>Warum nicht defineCustomElements() aus @npm9912/v-map/loader?
Stencils Lazy-Loader benutzt import.meta.url zur Laufzeit, um seine *.entry.js Chunks zu finden. Wenn Angulars esbuild-Build den Loader mit-bündelt, landen diese Chunks unter /main-XYZ.js und Stencil 404t auf jeden Layer-Chunk.
Mit dem <script type="module">-Tag von jsDelivr läuft v-map als ungebündeltes ES-Modul direkt im Browser, import.meta.url zeigt auf die jsDelivr-CDN-URL und Stencil findet seine Chunks da auch.
3. Custom Elements vor dem Bootstrap abwarten
ts
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
async function bootstrap() {
// Warten bis v-map.esm.js geladen und alle Custom Elements registriert sind
await customElements.whenDefined('v-map');
await bootstrapApplication(AppComponent, appConfig);
}
bootstrap().catch(console.error);4. CUSTOM_ELEMENTS_SCHEMA setzen
Wichtigster Angular-spezifischer Schritt: das Template eines Components, das v-map-Tags verwendet, braucht CUSTOM_ELEMENTS_SCHEMA in schemas. Ohne das schlägt der Angular Compiler mit:
'v-map' is not a known element:
1. If 'v-map' is an Angular component, then verify that it is part of this module.
2. To allow any element add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
templateUrl: './app.component.html',
})
export class AppComponent {
zoom = signal(11);
}5. Erste Karte deklarativ
html
<!-- src/app/app.component.html -->
<input
type="range"
min="2"
max="18"
[value]="zoom()"
(input)="zoom.set($any($event.target).valueAsNumber)"
/>
<v-map flavour="ol" center="11.576,48.137" [attr.zoom]="zoom().toString()">
<v-map-error position="bottom-right" auto-dismiss="6000"></v-map-error>
<v-map-layergroup group-title="Base" basemapid="osm">
<v-map-layer-osm id="osm" label="OpenStreetMap"></v-map-layer-osm>
</v-map-layergroup>
</v-map>css
/* src/app/app.component.css */
v-map {
display: block;
width: 100%;
height: 70vh;
}Drag den Slider — die Karte zoomt direkt mit. Angular's Signal-System markiert die zoom-abhängigen Bindings als dirty und re-rendert die betroffenen Attribute am Custom-Element.
[attr.foo] vs [foo]
Wir binden [attr.zoom]="zoom().toString()" und nicht [zoom]="zoom()". Der [attr.X] Bind setzt das HTML-Attribut (immer String), während [X] die Property des Elements setzen würde — und Stencil's Attribute-Reflection erwartet beim ersten Mount das Attribut. Bei Number-Props wie zoom, opacity, z-index immer [attr.X] mit expliziter .toString()-Conversion verwenden.
6. Reactive Layer hinzufügen oder ausblenden
Mit Angular 17+'s neuem @if Control-Flow-Block:
html
<v-map flavour="ol">
<v-map-layergroup group-title="Base" basemapid="osm">
<v-map-layer-osm id="osm"></v-map-layer-osm>
</v-map-layergroup>
@if (showOverlay()) {
<v-map-layergroup group-title="Daten">
<v-map-layer-geojson url="/data/points.geojson"></v-map-layer-geojson>
</v-map-layergroup>
}
</v-map>
<button (click)="showOverlay.set(!showOverlay())">
Toggle GeoJSON Overlay
</button>v-map registriert / disposed den Layer automatisch, sobald Angular das Custom Element mountet bzw. unmountet.
7. Error-Events programmatisch konsumieren
vmap-error bubblet von jedem Layer zur <v-map> hoch. In Angular reicht ein (vmap-error) direkt im Template:
ts
interface VMapErrorDetail {
type: 'network' | 'validation' | 'parse' | 'provider';
message: string;
attribute?: string;
cause?: unknown;
}
@Component({
/* ... */
})
export class AppComponent {
onMapError(event: Event) {
const detail = (event as CustomEvent<VMapErrorDetail>).detail;
if (!detail) return;
console.error('[vmap]', detail.type, detail.message);
// hier z. B. in einen Service oder Store dispatchen
}
}html
<v-map flavour="ol" (vmap-error)="onMapError($event)">
...
</v-map>Für reines Toast-Display ohne JS-Listener reicht aber das deklarative <v-map-error> (siehe oben).
Zoneless Change Detection
Angular 18+ unterstützt provideExperimentalZonelessChangeDetection() als Provider in appConfig. Damit verzichtest du auf zone.js' Monkey-Patching der DOM-Events, was für die meisten Custom-Element-Events sauberer ist — Signals sind dann die einzige Quelle der Reactivity. Die Demo nutzt diese Strategie.
Vollständiger Code
Den kompletten Source der Live-Demo oben findest du im v-map-Repo unter examples/angular/. Der relevante Teil ist src/app/app.component.ts.
Stolperfallen
CUSTOM_ELEMENTS_SCHEMAvergessen. Compile-time-Error „is not a known element". Pflicht-Schritt für jedes Component, das v-map-Tags verwendet.[zoom]="zoom()"statt[attr.zoom]="zoom().toString()". Setzt die Property statt des Attributs. Funktioniert beim ersten Mount, aber spätere Updates greifen nicht zuverlässig — Stencil's Attribut-Reflection ist die zuverlässige API.defineCustomElements()aus dem Loader bündeln. Funktioniert in Angular's esbuild-Build NICHT (gleicher Bug wie React/Vue/SvelteKit). Immer den<script type="module">aus jsDelivr imindex.htmlverwenden.zone.jswickelt Custom-Element-Events. Wenn du zone.js aktiviert lässt, führt jedervmap-errorEvent Handler einen Change-Detection- Cycle aus. MitprovideExperimentalZonelessChangeDetection()und Signal-State entfällt das, was den Demo deutlich glatter macht.moduleResolutionim tsconfig. Angular 16+ braucht"moduleResolution": "bundler"(oder"node16"). Default"classic"führt zu „Cannot find module '@angular/core'" Errors.
Vergleich mit den anderen Frameworks
| Aspekt | Angular | Vue 3 | React 19 | SvelteKit |
|---|---|---|---|---|
| Reactive State | signal() | ref() | useState() | $state() |
| Slider-Binding | [value] (input) | v-model.number | value onChange | bind:value |
| Conditional | @if | v-if | {cond && ...} | {#if} |
| Custom Element Event | (vmap-error)="..." | @vmap-error="..." | useRef + addEventListener | onvmap-error={...} |
| Custom Element Setup | CUSTOM_ELEMENTS_SCHEMA | compilerOptions.isCustomElement | (kein Setup) | (kein Setup) |
| Bundle (gzipped) | ~48 KB | ~27 KB | ~62 KB | ~30 KB |
| Boilerplate | hoch | niedrig | mittel | sehr niedrig |
Angular hat den höchsten initialen Setup-Aufwand (NgModule oder standalone + schemas + tsconfig), aber für Teams mit Angular-Erfahrung sind die Patterns gewohnt und Signals + zoneless DC machen den Reactive-Pfad sehr ähnlich zu Vue 3.
Siehe auch
- Getting Started — generelles Setup
- SvelteKit-Guide — gleicher Showcase mit Svelte 5
- React-Guide — gleicher Showcase mit React 19
- Vue 3 Guide — gleicher Showcase mit Vue 3
- CDN ohne Bundler — wenn du gar keinen Build-Step willst
- Error Handling — die
<v-map-error>Komponente und dasvmap-errorEvent - Komponenten-API — alle 19 Web Components