我使用带有Angular 4的AGM库来开发组件。
该组件有4个输入:
1º输入:如果变量“ useGoogleMap”为false,则显示此输入,以便用户无需使用Google地图即可输入地址。
2º输入:当变量“ useGoogleMap”为true时,将显示AGM自动完成输入。
3º和4º输入:显示可编辑的经纬度输入,可在AGM地图上搜索。
问题是,如果我使用* ngIf =“ useGoogleMap”(和useGoogleMap = false),则AGM不会初始化,而当它使用useGoogleMap = true时,其未定义的地图可以使用(ngOnInit,ngOnChanges等)。
我也尝试过隐藏,但是隐藏或显示它不是自动的,只能在组件的某些输入的blur()下工作(不知道为什么)。
这是一些代码:
export class InputGooglePlacesComponent implements OnInit, OnChanges {
@ViewChild('googlePlaceInput') googlePlaceInputElementRef: ElementRef;
@ViewChild('map') map: AgmMap;
@ViewChild('marker') marker: AgmMarker;
@ViewChild('useGoogle') useGoogle: ElementRef;
@ViewChild('manualAddress') manualAddress: ElementRef;
@Input() public label: string;
@Input() public field: string;
@Input() public placeholder = 'Escribe una dirección';
@Input() public form: FormGroup;
@Output() selectedPlace = new EventEmitter();
@Input() public latitud: number = AppSettings.GMAPS_LOCATION_DEFAULT.lat;
@Input() public longitud: number = AppSettings.GMAPS_LOCATION_DEFAULT.lng;
@Input() public direccion: string = null;
public finishChanges = new EventEmitter();
private formatted_address: string;
public zoom = 12;
public autocomplete: any = null;
public useGoogleMap = false;
constructor(private mapsAPILoader: MapsAPILoader, private ngZone: NgZone) {}
ngOnInit() {
if (this.useGoogleMap) {
this.loadPlaces();
}
}
httpGet() {
const url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + this.direccion.replace(/ /g, '+') + '&key=' + AppSettings.GMAPS_API_KEY;
const xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", url, false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.response;
}
ngOnChanges(changeRecord: SimpleChanges) {
if (this.useGoogleMap) {
if (!this.latitud || !this.longitud) {
if (this.direccion && this.direccion !== '') {
//No viene la latitud o longitud, y la dirección viene por BD. Se setea como dirección manual.
this.manualAddress.nativeElement.value = this.direccion;
this.useGoogleMapAPI(false);
} else {
//No viene ninguno de los 3 campos (lat, lng, dirección). Se setea direccion gmap por defecto.
this.latitud = AppSettings.GMAPS_LOCATION_DEFAULT.lat;
this.longitud = AppSettings.GMAPS_LOCATION_DEFAULT.lng;
this.zoom = 9;
this.mapsAPILoader.load().then(() => {
let latlng;
latlng = new google.maps.LatLng(this.latitud, this.longitud);
new google.maps.Geocoder().geocode({ location: latlng }, (results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]) {
this.formatted_address = results[0].formatted_address;
}
this.selectedPlace.emit({
formatted_address: this.formatted_address,
latitud: this.latitud,
longitud: this.longitud,
coordinates: `POINT(${this.longitud} ${this.latitud})`
});
}
});
this.useGoogleMapAPI(true);
this.map.triggerResize();
});
}
} else {
if (this.direccion && this.direccion !== '') {
this.mapsAPILoader.load().then(() => {
const location = JSON.parse(this.httpGet()).results[0];
const latitud = Math.round(Math.abs(this.latitud) * 1000000) / 1000000;
const geolatitud = Math.round(Math.abs(location.geometry.location.lat) * 1000000) / 1000000;
const longitud = Math.round(Math.abs(this.longitud) * 1000000) / 1000000;
const geolongitud = Math.round(Math.abs(location.geometry.location.lng) * 1000000) / 1000000;
if ( Math.abs(latitud - geolatitud) <= 0.001 && Math.abs(longitud - geolongitud) <= 0.001) {
//Coincide la dirección con la georeferencia
this.initMarkerObservable({ coords: { lat: this.latitud, lng: this.longitud } }).subscribe(() => {
this.finishChanges.emit();
});
this.zoom = 12;
this.useGoogleMapAPI(true);
this.map.triggerResize();
} else {
//No coincide la dirección con la georeferencia
// this.latitud = null;
// this.longitud = null;
this.manualAddress.nativeElement.value = this.direccion;
this.manualAddress.nativeElement.blur();
this.useGoogleMapAPI(false);
}
});
} else {
this.latitud = AppSettings.GMAPS_LOCATION_DEFAULT.lat;
this.longitud = AppSettings.GMAPS_LOCATION_DEFAULT.lng;
this.zoom = 9;
this.mapsAPILoader.load().then(() => {
let latlng;
latlng = new google.maps.LatLng(this.latitud, this.longitud);
new google.maps.Geocoder().geocode({ location: latlng }, (results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]) {
this.formatted_address = results[0].formatted_address;
}
this.selectedPlace.emit({
formatted_address: this.formatted_address,
latitud: this.latitud,
longitud: this.longitud,
coordinates: `POINT(${this.longitud} ${this.latitud})`
});
}
});
this.useGoogleMapAPI(true);
this.map.triggerResize();
});
}
}
}
}
loadPlaces() {
this.mapsAPILoader.load().then(() => {
this. autocomplete = new google.maps.places.Autocomplete(this.googlePlaceInputElementRef.nativeElement, {
types: ['address']
});
this.autocomplete.addListener('place_changed', () => {
this.ngZone.run(() => {
const place: google.maps.places.PlaceResult = this.autocomplete.getPlace();
if (place.geometry === undefined || place.geometry === null) {
return;
}
this.latitud = place.geometry.location.lat();
this.longitud = place.geometry.location.lng();
this.selectedPlace.emit({
formatted_address: place.formatted_address,
latitud: place.geometry.location.lat(),
longitud: place.geometry.location.lng(),
coordinates: `POINT(${place.geometry.location.lng()} ${place.geometry.location.lat()})`
});
});
});
});
}
clickMarker(event: any) {
if (this.useGoogleMap) {
this.latitud = event.coords.lat;
this.longitud = event.coords.lng;
let latlng;
latlng = new google.maps.LatLng(this.latitud, this.longitud);
new google.maps.Geocoder().geocode({ location: latlng }, (results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]) {
this.formatted_address = results[0].formatted_address;
}
this.selectedPlace.emit({
formatted_address: this.formatted_address,
latitud: this.latitud,
longitud: this.longitud,
coordinates: `POINT(${this.longitud} ${this.latitud})`
});
}
});
}
}
initMarkerObservable(event: any): Observable<any> {
if (this.useGoogleMap) {
return new Observable<any>(observer => {
this.latitud = event.coords.lat;
this.longitud = event.coords.lng;
let latlng;
latlng = new google.maps.LatLng(this.latitud, this.longitud);
const promise = new Promise((resolve, reject) => {
new google.maps.Geocoder().geocode({ location: latlng }, (results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]) {
this.formatted_address = results[0].formatted_address;
}
this.selectedPlace.emit({
formatted_address: this.formatted_address,
latitud: this.latitud,
longitud: this.longitud,
coordinates: `POINT(${this.longitud} ${this.latitud})`
});
resolve();
}
});
}).then(() => {
observer.next('');
observer.complete();
});
});
}
}
triggerResize() {
if (this.useGoogleMap) {
this.map.triggerResize();
}
}
resetPlaces() {
if (this.useGoogleMap) {
this.googlePlaceInputElementRef.nativeElement.value = '';
}
}
setUseGoogle() {
this.useGoogleMap = this.useGoogle.nativeElement.checked;
if (this.useGoogleMap) {
this.loadPlaces();
this.map.triggerResize();
}
}
useGoogleMapAPI(checked: boolean) {
this.useGoogle.nativeElement.checked = checked;
this.setUseGoogle();
}
recalculatePosition(event: any) {
if (this.useGoogleMap) {
if (event.srcElement.id === 'mapLatitud') {
this.latitud = Number(event.srcElement.value);
} else if (event.srcElement.id === 'mapLongitud') {
this.longitud = Number(event.srcElement.value);
}
this.clickMarker({ coords: { lat: this.latitud, lng: this.longitud } });
}
}
}
agm-map {
height: 300px;
}
.coords__input {
margin: 10px 0;
}
<div class="form-group" [formGroup]="form">
<div class="row">
<div class="col-md-6"><label [for]="field">{{ label }}</label></div>
<div class="col-md-6"><label for="useGoogle" ><input id="useGoogle" type="checkbox" (change)="setUseGoogle()" #useGoogle/> Usar google maps</label></div>
</div>
<div *ngIf="!useGoogleMap">
<input [type]="text" class="form-control form-control-sm"
[id]="field" [placeholder]="placeholder"
[autocapitalize]="autocapitalize" [spellcheck]="spellcheck" #manualAddress>
</div>
<div *ngIf="useGoogleMap">
<input
[id]="field" [placeholder]="placeholder" [formControlName]="field"
type="text"
class="form-control form-control-sm"
#googlePlaceInput />
<div class="row">
<div class="col-md-6">
<input id="mapLatitud" type="number" class="form-control form-control-sm coords__input" [value]="latitud" (blur)="recalculatePosition($event)"/>
</div>
<div class="col-md-6">
<input id="mapLongitud" type="number" class="form-control form-control-sm coords__input" [value]="longitud" (blur)="recalculatePosition($event)"/>
</div>
</div>
<agm-map
[usePanning]="true"
[latitude]="latitud"
[longitude]="longitud"
[zoom]="zoom"
(mapClick)=clickMarker($event) #map>
<agm-marker [latitude]="latitud" [longitude]="longitud" #marker>
</agm-marker>
</agm-map>
<app-form-control-errors [form]="form" [field]="field" [singleInput]="googlePlaceInput">
</app-form-control-errors>
</div>
</div>
我希望有人能帮助我。
我将更新任何进度。
最佳答案
一些解释:
如果在组件使用期内“ useGoogleMap”输入未更改,则可以访问ngAfterViewInit中的HTML(@ViewChild)。
如果您的值在组件生命周期中发生变化(因此可以多次调用ngOnChange),请使用setTimeout(()=> {...},0);围绕您的视图操作。在执行此操作之前,它将等待其他所有操作完成。
有关标题的更多信息:隐藏与所有浏览器都不兼容,因此在动态类中建议使用样式为“ display:none”的* ngIf或[ngClass]。