Managing Model Collections
In AurOpenlayers, the map is a reflection of your data. Instead of manipulating OpenLayers Feature objects directly, you manage a collection of domain models through the VectorLayerApi. The framework handles the heavy lifting of creating, updating, and removing the corresponding geometries on the map.
Accessing the Layer API
To manage a collection, you first need to grab the API instance for the specific layer from the MapContext. This is typically done in the onReady handler of your component.
private pointLayerApi?: VectorLayerApi<MapPoint, Geometry>;
onReady(ctx: MapContext): void {
// Access the layer by the ID defined in your MapSchema
this.pointLayerApi = ctx.layers['points-layer-id'];
}
Basic Collection Operations
The VectorLayerApi provides several methods to synchronize your application state with the map.
Setting the Initial State
Use setModels() to replace the entire collection. This is common when loading data from a server.
const initialPoints = [
{ id: '1', lat: 53.9, lng: 27.5, name: 'Minsk' },
{ id: '2', lat: 53.3, lng: 26.6, name: 'Stolbtsy' }
];
this.pointLayerApi?.setModels(initialPoints);
Adding and Removing Models
You can modify the collection incrementally without reloading everything.
// Add a single model
this.pointLayerApi?.addModel({ id: '3', lat: 52.4, lng: 31.0, name: 'Gomel' });
// Add multiple models at once
this.pointLayerApi?.addModels([pointA, pointB]);
// Remove specific models by their IDs
this.pointLayerApi?.removeModelsById(['1', '2']);
// Clear the entire layer
this.pointLayerApi?.clear();
Handling Duplicate IDs
To maintain a reliable link between your data and the map features, every model in a layer must have a unique ID (as defined by the feature.id function in your descriptor).
If you attempt to add a model with an ID that already exists in the layer, the framework throws a DuplicateModelIdError. This prevents silent bugs where features might overlap or become unreachable.
try {
this.pointLayerApi?.addModel(duplicatePoint);
} catch (error) {
if (error instanceof DuplicateModelIdError) {
console.error(`Model ${error.id} already exists in layer ${error.layerId}`);
}
}
Listening for Collection Changes
Whenever the collection is modified (models added, removed, or the list reset), the onModelsCollectionChanged event fires. This is useful for updating UI counters or side panels.
this.pointLayerApi?.onModelsCollectionChanged((event) => {
console.log('Reason:', event.reason); // 'set' | 'add' | 'remove' | 'clear'
console.log('Previous count:', event.prev.length);
console.log('New count:', event.next.length);
});
Handling Model Mutations (Updates)
While onModelsCollectionChanged tracks membership, onModelsChanged tracks internal changes to models, such as when a user drags a point on the map.
When a interaction (like translate) changes a model's geometry, the framework calculates the "next" version of your model using your applyGeometryToModel logic and emits an event.
Example: Syncing a Route Line
If you have a route line that depends on a list of points, you should listen for changes to those points to redraw the line.
this.pointLayerApi?.onModelsChanged?.((changes) => {
// 'changes' is an array of { prev, next, reason }
// Update your local state with the new versions of the models
this.points = this.pointLayerApi.getAllModels();
// Re-sync other layers that depend on this data
this.routeLayerApi?.setModels([new RouteLine(this.points)]);
});
Immutability and Snapshots
All data returned by the API is immutable. When you call getAllModels(), you receive a shallow copy (snapshot) of the current state.
- Preserved Order: The framework maintains the order in which models were added.
- Predictable State: Modifying an object inside the array returned by
getAllModels()will not automatically update the map. You must always pass the updated object back through the API (e.g., viasetModelsor by handlingonModelsChangedresults) to trigger a re-render.
Practical Walkthrough: Adding a "Draft" Point
A common pattern is creating a "draft" feature that the user can move before saving it to a primary collection.
- Create a Draft Layer: Define a dedicated layer in your schema for the draft.
- Add on Click: Use a map click event to call
draftLayerApi.setModels([newPoint]). - Enable Interaction: Allow the user to drag the draft point.
- Save: On a "Save" button click, take the model from the draft layer and move it to your main layer.
// 1. Get the draft point from the draft layer
const draft = this.draftLayerApi?.getAllModels()[0];
if (draft) {
// 2. Move to the permanent route layer
this.routePointsLayerApi?.addModel(draft);
// 3. Clear the draft
this.draftLayerApi?.clear();
}