diff --git a/docs/content/guides/js-marker-clusterer/markers-cluster-with-js-marker-clusterer.md b/docs/content/guides/js-marker-clusterer/markers-cluster-with-js-marker-clusterer.md index 21beb35da..6a0c58660 100644 --- a/docs/content/guides/js-marker-clusterer/markers-cluster-with-js-marker-clusterer.md +++ b/docs/content/guides/js-marker-clusterer/markers-cluster-with-js-marker-clusterer.md @@ -53,4 +53,16 @@ When you import the `AgmJsMarkerClustererModule`, you can use the `agmMarkerClus -``` \ No newline at end of file +``` + +### Specifying a custom calculator + +If you want to control how the cluster style is calculated, you can pass in a `CalculateFunction` via `calculator`: + +```html + + + + + +``` diff --git a/packages/js-marker-clusterer/directives/marker-cluster.ts b/packages/js-marker-clusterer/directives/marker-cluster.ts index 7af0be6a1..767ed33e8 100644 --- a/packages/js-marker-clusterer/directives/marker-cluster.ts +++ b/packages/js-marker-clusterer/directives/marker-cluster.ts @@ -3,7 +3,7 @@ import {Directive, Input, OnDestroy, OnChanges, OnInit, SimpleChange} from '@ang import {ClusterManager} from '../services/managers/cluster-manager'; import {MarkerManager, InfoWindowManager} from '@agm/core'; -import {ClusterOptions, ClusterStyle} from '../services/google-clusterer-types'; +import {CalculateFunction, ClusterOptions, ClusterStyle} from '../services/google-clusterer-types'; /** * AgmMarkerCluster clusters map marker if they are near together @@ -71,6 +71,11 @@ export class AgmMarkerCluster implements OnDestroy, OnChanges, OnInit, ClusterOp */ @Input() styles: ClusterStyle; + /** + * A function that calculates the cluster style and text based on the markers in the cluster. + */ + @Input() calculator: CalculateFunction; + @Input() imagePath: string; @Input() imageExtension: string; @@ -110,6 +115,9 @@ export class AgmMarkerCluster implements OnDestroy, OnChanges, OnInit, ClusterOp if (changes['imageExtension']) { this._clusterManager.setImageExtension(this); } + if (changes['calculator']) { + this._clusterManager.setCalculator(this); + } } /** @internal */ diff --git a/packages/js-marker-clusterer/services/managers/cluster-manager.spec.ts b/packages/js-marker-clusterer/services/managers/cluster-manager.spec.ts index aac2d61b8..b954e2f8d 100644 --- a/packages/js-marker-clusterer/services/managers/cluster-manager.spec.ts +++ b/packages/js-marker-clusterer/services/managers/cluster-manager.spec.ts @@ -3,7 +3,7 @@ import {TestBed, async, inject} from '@angular/core/testing'; import {AgmMarker} from '../../../core/directives/marker'; import {GoogleMapsAPIWrapper} from '../../../core/services/google-maps-api-wrapper'; -import {Marker} from '../../../core/services/google-maps-types'; +import {AgmMarkerCluster} from '../../directives/marker-cluster'; import {ClusterManager} from './cluster-manager'; describe('ClusterManager', () => { @@ -207,4 +207,34 @@ describe('ClusterManager', () => { () => { expect(markerInstance.setZIndex).toHaveBeenCalledWith(zIndex); }); }))); }); + + describe('set calculator', () => { + it('should call the setCalculator method when the calculator changes and is a function', + inject( + [ClusterManager], + async (markerManager: ClusterManager) => { + + const mockClusterer = { setCalculator: jest.fn() }; + const instancePromise = Promise.resolve(mockClusterer); + + const spy = jest.spyOn(markerManager, 'getClustererInstance') + .mockImplementation(() => instancePromise); + + const markerCluster: Partial = {}; + + // negative case + markerCluster.calculator = null; + markerManager.setCalculator(markerCluster as AgmMarkerCluster); + await instancePromise; + expect(mockClusterer.setCalculator).not.toHaveBeenCalled(); + + // positive case + markerCluster.calculator = jest.fn(); + markerManager.setCalculator(markerCluster as AgmMarkerCluster); + await instancePromise; + expect(mockClusterer.setCalculator).toHaveBeenCalledTimes(1); + + spy.mockRestore(); + })); + }); }); diff --git a/packages/js-marker-clusterer/services/managers/cluster-manager.ts b/packages/js-marker-clusterer/services/managers/cluster-manager.ts index 66feabf00..d063d72df 100644 --- a/packages/js-marker-clusterer/services/managers/cluster-manager.ts +++ b/packages/js-marker-clusterer/services/managers/cluster-manager.ts @@ -30,8 +30,12 @@ export class ClusterManager extends MarkerManager { }); } + getClustererInstance(): Promise { + return this._clustererInstance; + } + addMarker(marker: AgmMarker): void { - const clusterPromise: Promise = this._clustererInstance; + const clusterPromise: Promise = this.getClustererInstance(); const markerPromise = this._mapsWrapper .createMarker({ position: { @@ -65,7 +69,7 @@ export class ClusterManager extends MarkerManager { return m.then((m: Marker) => { this._zone.run(() => { m.setMap(null); - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { cluster.removeMarker(m); this._markers.delete(marker); }); @@ -74,31 +78,31 @@ export class ClusterManager extends MarkerManager { } clearMarkers(): Promise { - return this._clustererInstance.then(cluster => { + return this.getClustererInstance().then(cluster => { cluster.clearMarkers(); }); } setGridSize(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { cluster.setGridSize(c.gridSize); }); } setMaxZoom(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { cluster.setMaxZoom(c.maxZoom); }); } setStyles(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { cluster.setStyles(c.styles); }); } setZoomOnClick(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { if (c.zoomOnClick !== undefined) { cluster.zoomOnClick_ = c.zoomOnClick; } @@ -106,7 +110,7 @@ export class ClusterManager extends MarkerManager { } setAverageCenter(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { if (c.averageCenter !== undefined) { cluster.averageCenter_ = c.averageCenter; } @@ -114,7 +118,7 @@ export class ClusterManager extends MarkerManager { } setImagePath(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { if (c.imagePath !== undefined) { cluster.imagePath_ = c.imagePath; } @@ -122,7 +126,7 @@ export class ClusterManager extends MarkerManager { } setMinimumClusterSize(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { if (c.minimumClusterSize !== undefined) { cluster.minimumClusterSize_ = c.minimumClusterSize; } @@ -130,10 +134,18 @@ export class ClusterManager extends MarkerManager { } setImageExtension(c: AgmMarkerCluster): void { - this._clustererInstance.then(cluster => { + this.getClustererInstance().then(cluster => { if (c.imageExtension !== undefined) { cluster.imageExtension_ = c.imageExtension; } }); } + + setCalculator (c: AgmMarkerCluster): void { + this.getClustererInstance().then(cluster => { + if (typeof c.calculator === 'function') { + cluster.setCalculator(c.calculator); + } + }); + } }