Skip to content

fix(google-maps): allow for ground overlay image to be changed #19306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/dev-app/google-map/google-map-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,16 @@
</label>
</div>

<div>
<label for="ground-overlay-image">
Ground Overlay image

<select id="ground-overlay-image" (change)="groundOverlayUrlChanged($event)">
<option
*ngFor="let image of groundOverlayImages"
[value]="image.url">{{image.title}}</option>
</select>
</label>
</div>

</div>
16 changes: 15 additions & 1 deletion src/dev-app/google-map/google-map-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,17 @@ export class GoogleMapDemo {
circleOptions: google.maps.CircleOptions =
{center: CIRCLE_CENTER, radius: CIRCLE_RADIUS, strokeColor: 'grey', strokeOpacity: 0.8};
isGroundOverlayDisplayed = false;
groundOverlayUrl = 'https://angular.io/assets/images/logos/angular/angular.svg';
groundOverlayImages = [
{
title: 'Red logo',
url: 'https://angular.io/assets/images/logos/angular/angular.svg'
},
{
title: 'Black logo',
url: 'https://angular.io/assets/images/logos/angular/angular_solidBlack.svg'
}
];
groundOverlayUrl = this.groundOverlayImages[0].url;
groundOverlayBounds = RECTANGLE_BOUNDS;

mapTypeId: google.maps.MapTypeId;
Expand Down Expand Up @@ -149,4 +159,8 @@ export class GoogleMapDemo {
toggleGroundOverlayDisplay() {
this.isGroundOverlayDisplayed = !this.isGroundOverlayDisplayed;
}

groundOverlayUrlChanged(event: Event) {
this.groundOverlayUrl = (event.target as HTMLSelectElement).value;
}
}
30 changes: 28 additions & 2 deletions src/google-maps/map-ground-overlay/map-ground-overlay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ describe('MapGroundOverlay', () => {
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
});

it('has an error if required url or bounds are not provided', () => {
it('has an error if required bounds are not provided', () => {
expect(() => {
const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();
}).toThrow(new Error('An image url is required'));
}).toThrow(new Error('Image bounds are required'));
});

it('exposes methods that provide information about the Ground Overlay', () => {
Expand Down Expand Up @@ -119,6 +119,32 @@ describe('MapGroundOverlay', () => {
expect(addSpy).toHaveBeenCalledWith('dblclick', jasmine.any(Function));
subscription.unsubscribe();
});

it('should be able to change the image after init', () => {
const groundOverlaySpy = createGroundOverlaySpy(url, bounds, groundOverlayOptions);
const groundOverlayConstructorSpy =
createGroundOverlayConstructorSpy(groundOverlaySpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.componentInstance.url = url;
fixture.componentInstance.bounds = bounds;
fixture.componentInstance.clickable = clickable;
fixture.componentInstance.opacity = opacity;
fixture.detectChanges();

expect(groundOverlayConstructorSpy).toHaveBeenCalledWith(url, bounds, groundOverlayOptions);
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);

groundOverlaySpy.setMap.calls.reset();
fixture.componentInstance.url = 'foo.png';
fixture.detectChanges();

expect(groundOverlaySpy.set).toHaveBeenCalledWith('url', 'foo.png');
expect(groundOverlaySpy.setMap).toHaveBeenCalledTimes(2);
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(null);
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
});

});

@Component({
Expand Down
34 changes: 26 additions & 8 deletions src/google-maps/map-ground-overlay/map-ground-overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
private _eventManager = new MapEventManager(this._ngZone);

private readonly _opacity = new BehaviorSubject<number>(1);
private readonly _url = new BehaviorSubject<string>('');
private readonly _destroyed = new Subject<void>();

/**
Expand All @@ -37,13 +38,19 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
*/
groundOverlay?: google.maps.GroundOverlay;

@Input() url!: string; // Asserted in ngOnInit.

/** URL of the image that will be shown in the overlay. */
@Input()
bounds!: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral; // Asserted in ngOnInit.
set url(url: string) {
this._url.next(url);
}

@Input() clickable = false;
/** Bounds for the overlay. */
@Input() bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral;

/** Whether the overlay is clickable */
@Input() clickable: boolean = false;

/** Opacity of the overlay. */
@Input()
set opacity(opacity: number) {
this._opacity.next(opacity);
Expand All @@ -69,9 +76,6 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
constructor(private readonly _map: GoogleMap, private readonly _ngZone: NgZone) {}

ngOnInit() {
if (!this.url) {
throw Error('An image url is required');
}
if (!this.bounds) {
throw Error('Image bounds are required');
}
Expand All @@ -81,14 +85,16 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
// We'll bring it back in inside the `MapEventManager` only for the events that the
// user has subscribed to.
this._ngZone.runOutsideAngular(() => {
this.groundOverlay = new google.maps.GroundOverlay(this.url, this.bounds, options);
this.groundOverlay =
new google.maps.GroundOverlay(this._url.getValue(), this.bounds, options);
});
this._assertInitialized();
this.groundOverlay.setMap(this._map.googleMap!);
this._eventManager.setTarget(this.groundOverlay);
});

this._watchForOpacityChanges();
this._watchForUrlChanges();
}
}

Expand Down Expand Up @@ -150,6 +156,18 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
});
}

private _watchForUrlChanges() {
this._url.pipe(takeUntil(this._destroyed)).subscribe(url => {
this._assertInitialized();
const overlay = this.groundOverlay;
overlay.set('url', url);

// Google Maps only redraws the overlay if we re-set the map.
overlay.setMap(null);
overlay.setMap(this._map.googleMap!);
});
}

private _assertInitialized(): asserts this is {groundOverlay: google.maps.GroundOverlay} {
if (!this._map.googleMap) {
throw Error(
Expand Down
3 changes: 3 additions & 0 deletions src/google-maps/testing/fake-google-map-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,18 @@ export function createCircleConstructorSpy(circleSpy: jasmine.SpyObj<google.maps
export function createGroundOverlaySpy(
url: string, bounds: google.maps.LatLngBoundsLiteral,
options?: google.maps.GroundOverlayOptions): jasmine.SpyObj<google.maps.GroundOverlay> {
const values: {[key: string]: any} = {url};
const groundOverlaySpy = jasmine.createSpyObj('google.maps.GroundOverlay', [
'addListener',
'getBounds',
'getOpacity',
'getUrl',
'setMap',
'setOpacity',
'set',
]);
groundOverlaySpy.addListener.and.returnValue({remove: () => {}});
groundOverlaySpy.set.and.callFake((key: string, value: any) => values[key] = value);
return groundOverlaySpy;
}

Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/google-maps/google-maps.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export declare class MapGroundOverlay implements OnInit, OnDestroy {
mapClick: Observable<google.maps.MouseEvent>;
mapDblclick: Observable<google.maps.MouseEvent>;
set opacity(opacity: number);
url: string;
set url(url: string);
constructor(_map: GoogleMap, _ngZone: NgZone);
getBounds(): google.maps.LatLngBounds;
getOpacity(): number;
Expand Down