Reputation: 4952
Specifically, how do you show information related to the marker in the respective infoWindow?
By following the example on the repo here, the same information is opened for every marker. With the old angular google maps you could easily just insert the info window inside the marker with whatever data you wanted.
I saw a few examples of setting the content with {{ contentHere }} in the template, but that only allows for strings. I need to use all the angular template stuff like usual.
Let's say our template looks something like the repo example:
<google-map height="400px"
width="750px"
[center]="center"
[zoom]="zoom">
<map-marker #marker
*ngFor="let place of places"
[position]="{ lat: place.latitude, lng: place.longitude }"
[options]="markerOptions"
(mapClick)="openInfoWindow(marker)">
<map-info-window>
<-- this is what i'm concerned with -->
{{ place.description }}
</map-info-window>
</map-marker>
</google-map>
And then we add viewchild like the repo example and open it in the same way:
@ViewChild(MapInfoWindow, {static: false}) infoWindow: MapInfoWindow;
openInfoWindow(marker: MapMarker) {
this.infoWindow.open(marker);
}
Although this does open the correct marker and the marker will reflect dynamic info, this will show the same content / place.description for every info window, which isn't really surprising since a single ViewChild is being used. I've come up with all kinds of convoluted ways to do this and had a look through the source code but there doesn't seem to be an obvious and/or built in solution for displaying content in the window that is related to what is being used by the marker. It just seems weird to not have that as a basic example.
How would any of you go about doing this?
Upvotes: 13
Views: 16350
Reputation: 47
I share with you my solution in @angular/[email protected]. / Les comparto mi solución en @angular/[email protected], espero pueda ser de utilidad para alguien si es el caso
check this souce: https://github.com/angular/components/blob/16.2.x/src/google-maps/README.md
remember chech your Angular version branch
@angular/[email protected]
import { Component, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { iglesiasAsociadas } from './shared/data/iglesiasasociadas.data';
import {MapInfoWindow, MapMarker} from '@angular/google-maps';
import { PointMap } from './shared/model/pointmap';
@Component({
selector: 'app-mapadev',
templateUrl: './mapadev.component.html',
styleUrls: ['./mapadev.component.css']
})
export class MapadevComponent {
@ViewChild(MapInfoWindow, { static: false }) infoWindow!: MapInfoWindow;
@ViewChildren(MapMarker) markers!: QueryList<MapMarker>;
pints: PointMap[] = [];
infoContent: string = "";
apiLoaded: Observable<boolean>;
constructor(httpClient: HttpClient) {
this.apiLoaded = httpClient.jsonp('https://maps.googleapis.com/maps/api/js?key=YOUR_APIkEY', 'callback')//main
.pipe(
map(() => true),
catchError(() => of(false)),
);
}
options: google.maps.MapOptions = {
center: {lat: 40, lng: -80},
zoom: 4
};
center: google.maps.LatLngLiteral = {lat: 40, lng: -20};
zoom = 2;
markerOptions: google.maps.MarkerOptions = {draggable: false};
ngOnInit(): void {
}
markerPositions: google.maps.LatLngLiteral[] = iglesiasAsociadas.map(data=>{
return {lat: data.location[0], lng: data.location[1]}
});
points: PointMap[] = iglesiasAsociadas.map(data=>{
return {
title: data.title,
position:{lat: data.location[0], lng: data.location[1]},
info:
`<div>
<H1>${data.title}</H1>
<p>Dirección: ${data.direccion}</p>
<p>Liderazgo: ${data.liderazgo}</p>
<p>Movil: ${data.movil}</p>
<p>Email: ${data.email}</p>
<p>Webpage: ${data.webpage}</p>
</div>`
}
});
addMarker(event: google.maps.MapMouseEvent ) {
if ((event.latLng!=null) && !(event==null))this.markerPositions.push(event.latLng.toJSON());
}
openInfoWindow(marker: MapMarker, title: string, markerInfo: string) {
let positonsMarker = marker.getPosition()?.toString();
console.log(positonsMarker?.split(", "))
console.log(marker)
console.log(title, markerInfo);
this.infoContent = markerInfo;
this.infoWindow.open(marker);
};
}
// I did make in other file and import in this file to used it. // Yo cree el modelo en otro archivo para poder utilizarlo aquí
`// export class PointMap {
// title: string="";
// position: any; // {lat, lng} object - in accordance to the API
// info: string=""; // HTML-formatted on server side in my case, but you may construct the content here from a number of plain string data
// }`
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.2/angular.min.js"></script>
<p>mapadev works!</p>
<div *ngIf="apiLoaded | async">
<google-map
height="400px"
width="750px"
[center]="center"
[zoom]="zoom"
>
<map-marker
#marker="mapMarker"
*ngFor="let point of points"
[position]="point.position"
[title]="point.title"
[options]="markerOptions"
(mapClick)="openInfoWindow(marker, point.title, point.info)"
></map-marker>
<map-info-window [innerHTML]="infoContent" > Info Window content</map-info-window>
</google-map>
</div>
Upvotes: 0
Reputation: 1
Acept two arguments on openWindow function, I define a property as MarkerLabel in my Marcador interface in order to put a label, but you can set as string with the content and a property as LatLngLiteral to use to create the marker. Set the content using the infowindow View Child before open.
openInfoWindow method:
openInfoWindow(marker: MapMarker,markerPosition:Marcador) {
this.infoWindow.infoWindow?.setContent(`${markerPosition.label.text}`)
this.infoWindow.open(marker);
}
Map Marker:
<map-marker #marker="mapMarker" *ngFor="let markerPosition of marcadores"
[position]="markerPosition.position"
(mapClick)="openInfoWindow(marker,markerPosition)"
></map-marker>
<map-info-window></map-info-window
export interface Marcador{
label: google.maps.MarkerLabel,
position: google.maps.LatLngLiteral
}
Upvotes: 0
Reputation: 325
Most answers here are incorrect. Either
What I like most is the solution Warren has proposed. My concept is similar and as simple as that:
TS
@ViewChild(MapInfoWindow, { static: false }) infoWindow: MapInfoWindow;
@ViewChildren(MapMarker) markers: QueryList<MapMarker>;
points: PointMap[] = [];
infoContent: string = "";
options = {
scrollwheel: false,
disableDoubleClickZoom: true,
maxZoom: 15,
minZoom: 6,
};
ngOnInit() {
// points: PointMap[] array is filled up here
}
openInfoWindow(windowIndex: number, content: string) {
this.markers.forEach((marker: MapMarker, ix: number) => {
if (windowIndex === ix) {
this.infoContent = content;
this.infoWindow.open(marker);
}
});
}
My PointMap type (an array or list of these should come from the server side):
export class PointMap {
title: string;
position: any; // {lat, lng} object - in accordance to the API
info: string; // HTML-formatted on server side in my case, but you may construct the content here from a number of plain string data
}
HTML:
<google-map [center]="center" width="100%" height="500px" [options]="options">
<map-marker *ngFor="let marker of points; let ix = index
[position]="marker.position" [title]="marker.title"
(mapClick)="openInfoWindow(ix, marker.info)">
</map-marker>
<map-info-window [innerHTML]="infoContent"></map-info-window>
</google-map>
Believe me, this does work.
Upvotes: 7
Reputation: 21
there is a really easy way to sort this, looking at this call:
(mapClick)="openInfoWindow(marker)"
can be updated to pass a variable, referene or other details possibly even the content i.e.
(mapClick)="openInfoWindow(marker, reference || object || content)"
i.e. code I use regularly:
<google-map #map
height="90%"
width="100%"
[zoom]="zoom"
[center]="center"
[options]="options"
(boundsChanged)="mapBoundsChanged()"
>
<map-marker
#marker="mapMarker"
*ngFor="let location of filteredLocations"
[position]="{lat:location.locationLatitude,lng:location.locationLongitude }"
[title]="location.locationName"
[icon]="env.assetsUrlBase+locationTypeMap.get(location.locationTypeId)"
(mapClick)="openInfo(marker, location)"
>
</map-marker>
<map-info-window [innerHTML]="infoContent" #infoWindow="mapInfoWindow"></map-info-window>
</google-map>
This way you do not need to repeat the map or info windows and only the markers.
I would also advise that you id your children and call them in your component.ts by the id ensuring that the id in the html states their type.
This also allows for multiple maps on a single view that can behave independently.
i.e.
@ViewChild('map', { static: true }) map: GoogleMap;
@ViewChild('infoWindow', { static: true }) infoWindow: MapInfoWindow
@ViewChild('businessMap', { static: true }) businessMap: GoogleMap;
@ViewChild('businessInfoWindow', { static: true }) businessInfoWindow: MapInfoWindow
Upvotes: 2
Reputation: 3
Angular, doesn't want to display HTML code in a binding because, basically, it's a string binding (and for security reasons). But, instead of :
<map-info-window>
<-- this is what i'm concerned with -->
{{ place.description }}
</map-info-window>
You can simply do :
<map-info-window [innerHTML="place.description"]>
</map-info-window>
However, I didn't find a way to style that (even inline CSS doesn't seem to work). But basic formatting with <table>
works fine.
Upvotes: 0
Reputation: 4952
Use ViewChildren, use index in the for loop in the template, pass in the index when marker is clicked, loop through all ViewChildren using an index inside of a forEach (for loop with index will not open the window for some reason), then if the index of the loop matches the index passed in, open that window. It seems like there would be a better way than this but it's all I could come up with after trying many things:
In component:
@ViewChildren(MapInfoWindow) infoWindowsView: QueryList<MapInfoWindow>;
openInfoWindow(marker: MapMarker, windowIndex: number) {
/// stores the current index in forEach
let curIdx = 0;
this.infoWindowsView.forEach((window: MapInfoWindow) => {
if (windowIndex === curIdx) {
window.open(marker);
curIdx++;
} else {
curIdx++;
}
});
}
In template:
<google-map height="400px"
width="750px"
[center]="center"
[zoom]="zoom">
<map-marker #marker
*ngFor="let place of places; let i = index" <-- added index -->
[position]="{ lat: place.latitude, lng: place.longitude }"
[options]="markerOptions"
(mapClick)="openInfoWindow(marker, i)"> <-- pass in index -->
<map-info-window>
{{ place.description }}
</map-info-window>
</map-marker>
</google-map>
This will open the info window with its respective marker.
Upvotes: 14
Reputation: 131
Simplest way, hope it can help
TS
public openInfoWindow(marker: MapMarker, infoWindow: MapInfoWindow) {
infoWindow.open(marker);
}
HTML
<google-map *ngIf="mapOptions.center" width="100%" height="100%" [options]="mapOptions">
<ng-container *ngFor="let elem of elements">
<map-marker #marker="mapMarker" [position]="elem.position" [options]="elem.markerOptions" (mapClick)="openInfoWindow(marker, infoWindow)"></map-marker>
<map-info-window #infoWindow="mapInfoWindow"> Content of {{ elem.id }} ...</map-info-window>
</ng-container>
</google-map>
Upvotes: 13
Reputation: 1182
Another method is to modify the openInfoWindow
function to take a string parameter, and use this string to display in the map-info-window
.
In the component file.
export class AppComponent implements OnInit {
@ViewChild(MapInfoWindow, { static: false }) infoWindow: MapInfoWindow;
infoContent: string;
openInfo(marker: MapMarker, content: string) {
this.infoContent = content;
this.infoWindow.open(marker);
}
}
In the template.
<google-map
height="500px"
width="100%"
[zoom]="zoom"
[center]="center"
[options]="options"
(mapClick)="click($event)"
>
<map-marker
#markerElem
*ngFor="let marker of markers"
[position]="marker.position"
[label]="marker.label"
[title]="marker.title"
[options]="marker.options"
(mapClick)="openInfo(markerElem, marker.info)"
>
</map-marker>
<map-info-window>{{ infoContent }}</map-info-window>
</google-map>
Upvotes: 1