Popups and Overlays
Popups and overlays in AurOpenlayers allow you to display rich, interactive HTML content over the map. Instead of managing complex OpenLayers Overlay objects manually, you can use the declarative interaction system and the Popup Host pattern.
This guide shows how to implement a hover-triggered popup for your map features.
1. Define the Popup Host
The framework is designed to work with standard HTML elements as popup containers. First, create a container in your component's template. Using a separate container allows you to style it easily with CSS.
<div class="map-wrapper">
<!-- The Map -->
<mff-map-host [config]="mapConfig" (ready)="onReady($event)"></mff-map-host>
<!-- The Popup Host -->
<div #popupHost class="map-popup-host" [style.display]="isVisible ? 'block' : 'none'">
<!-- Content will be injected here or controlled via Angular bindings -->
<div [innerHTML]="popupContent"></div>
</div>
</div>
2. Configure the Interaction in MapSchema
To trigger a popup, you need to define interactions (like hover or click) within your VectorLayerDescriptor. This keeps the interaction logic tied to the specific layer.
In your mapConfig:
{
id: 'points-layer',
feature: {
// ... id and geometry config
interactions: {
hover: {
enabled: () => true,
// Triggered when the mouse enters a feature
onPointerEnter: ({ model, event }) => {
this.showPopup(model, event.pixel);
},
// Triggered when the mouse leaves a feature
onPointerLeave: () => {
this.hidePopup();
}
}
}
}
}
3. Implement the Popup Logic
In your component, implement the methods to handle the popup visibility and positioning. You can use the pixel provided by the interaction event to position the host element.
import { escapeHtml } from 'aur-openlayers'; // Utility for safety
// ... inside the component
popupContent: string = '';
isVisible = false;
@ViewChild('popupHost') popupHostElement!: ElementRef<HTMLDivElement>;
showPopup(model: MyDataModel, pixel: number[]): void {
// 1. Prepare safe HTML content
const safeName = escapeHtml(model.name);
this.popupContent = `<strong>${safeName}</strong><br>ID: ${model.id}`;
// 2. Position the element
const host = this.popupHostElement.nativeElement;
host.style.left = `${pixel[0]}px`;
host.style.top = `${pixel[1] - 10}px`; // Offset slightly above cursor
this.isVisible = true;
}
hidePopup(): void {
this.isVisible = false;
}
4. Using HTML Escape Utilities
When rendering model data into a popup's innerHTML, always sanitize the strings to prevent XSS attacks. The library provides a escapeHtml utility for this purpose.
import { escapeHtml } from 'aur-openlayers/public-utils/html-escape.utils';
const rawData = "<img src=x onerror=alert(1)>";
const safeData = escapeHtml(rawData); // Safely converted to entities
5. Styling the Popup
Since the popup host is just a standard div, you can use your component's SCSS to style it. Use position: absolute and pointer-events: none (if you don't want the popup itself to block map interactions).
.map-wrapper {
position: relative;
width: 100%;
height: 500px;
}
.map-popup-host {
position: absolute;
z-index: 10;
pointer-events: none; // Allows clicking "through" the popup
background: white;
padding: 8px 12px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
transform: translate(-50%, -100%); // Center horizontally and place above pixel
white-space: nowrap;
}
Advanced: Dynamic Angular Templates
If your popup requires complex Angular logic (buttons, pipes, or components), instead of innerHTML, you can use ngTemplateOutlet:
- Define an
<ng-template #popupTemplate>in your HTML. - Store the
selectedModelin your component whenonPointerEnterfires. - The popup host will automatically update based on the Angular change detection cycle.
<div #popupHost
class="map-popup-host"
*ngIf="selectedModel"
[style.left.px]="currentPixel[0]"
[style.top.px]="currentPixel[1]">
<ng-container *ngTemplateOutlet="popupTemplate; context: { $implicit: selectedModel }">
</ng-container>
</div>
<ng-template #popupTemplate let-model>
<div class="custom-popup">
<h4>{{ model.name | uppercase }}</h4>
<button (click)="onDetails(model.id)">View Details</button>
</div>
</ng-template>
Note: When using Angular templates for popups, ensure you use
NgZonecorrectly if the map events trigger updates outside of Angular's zone. Most AurOpenlayers interaction handlers are already wrapped to trigger change detection.