Я создаю приложение с картой на главном экране. Всякий раз, когда пользователь нажимает на маркер, он должен открывать div с абсолютным позиционированием в нижней части экрана, с информацией о месте нажатия. Проблема в том, что видимость и положение div управляются ngIf (чтобы он был видимым) и ngClass (чтобы он скользил вверх). При щелчке по булавке прослушиватель событий карт Google запускается и вызывает функцию, которую я определил для события щелчка маркера. Эта функция изменяет состояние переменных, используемых для ngClass и ngIf. При щелчке по маркеру эта функция запускается немедленно, но я вижу только то, как отображается нижний блок, только после того, как я щелкну по другому элементу главного экрана. Это поведение было проверено в инструменте Chrome Inspector, состояние ngIf и ngClass изменяются как раз в тот момент, когда я нажимаю на любой другой элемент на экране. Используемая карта является реализацией JS Google Maps. Я успешно использовал реализацию agm, но мне нужна была поддержка пользовательских нарисованных маркеров, и я не мог заставить ее работать с agm.
Вот код для домашнего экрана:
<ion-content>
<div class="places">
<ion-list has-header>
<ion-item>
<ion-label style="margin: 0px !important;">Lugares</ion-label>
<ion-select interface="popover">
<ion-option *ngFor="let place of placesCombo" [value]="palce" [selected]="place=='Perto de mim'">{{place}}</ion-option>
<!-- (ionSelect)="onTypeChange(place)" -->
</ion-select>
</ion-item>
</ion-list>
<ion-searchbar class="home-search" [(ngModel)]="searchQuery" [showCancelButton]="true" (keyup.enter)="closeKeyboard()" (ionInput)="onSearchbarInput($event)"
(ionCancel)="onCancel($event)" placeholder="Buscar um lugar"></ion-searchbar>
<ion-list class="places-list" *ngIf="showList">
<button ion-item *ngFor="let place of searchPlaces" (click)="centerPlace(place)">
{{ place.name }}
</button>
</ion-list>
</div>
<div class="map-wrap">
<div #map id="map"></div>
<div *ngIf="placeInfo != null" class="establishment-card" [ngClass]="{'active': isCardShown }">
<div class="bg" [ngStyle]="getStyle()">
</div>
<button ion-button round class="close" (click)="dismissCard(this)">
<ion-icon class="icon-close"></ion-icon>
</button>
<div card-content no-lines no-padding>
<h1 text-center>{{getName()}}</h1>
<ion-icon text-center white *ngFor="let item of starData; let i = index">
<i icon-medium *ngIf="starData[i].state == 'empty'" class="icon icon-star-outline"></i>
<i icon-medium *ngIf="starData[i].state == 'half'" class="icon icon-star-half"></i>
<i icon-medium *ngIf="starData[i].state == 'full'" class="icon icon-star"></i>
</ion-icon>
<div class="reviews" span-small text-center margin-bottom>{{getRate()}} (78 avaliações)</div>
<button ion-button block icon-start (click)="checkOffers()">
<ion-icon name="pricetags"></ion-icon>OFERTAS</button>
<button ion-button block icon-start (click)="checkDetails()">
<ion-icon name="search"></ion-icon>VER MAIS</button>
<button ion-button block icon-start (click)="navigate()">
<ion-icon name="navigate"></ion-icon>IR AGORA</button>
</div>
</div>
</div>
Этот экран является одной из страниц в макете с тремя вкладками. Я не думаю, что это может усугубить ситуацию, но чем больше мы знаем, тем лучше.
Это класс TS для домашнего экрана:
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, AlertController, ActionSheetController, Keyboard } from 'ionic-angular';
import { Geolocation } from '@ionic-native/geolocation';
import { Http, URLSearchParams } from '@angular/http';
import { GlobalVars } from '../../../providers/global-variables';
import { Establishment } from '../../../models/establishment';
import { AlertUtils } from '../../../utils/alert-utils';
import { NavUtils } from '../../../utils/nav-utils';
import { EstablishmentDetailsPage } from '../../establishment-details/establishment-details';
import { LaunchNavigator } from '../../../../node_modules/@ionic-native/launch-navigator';
import { EstablishmentOffersPage } from '../../establishment-offers/establishment-offers';
import { Diagnostic } from '@ionic-native/diagnostic';
import { Storage } from '../../../../node_modules/@ionic/storage';
import { MapUtils } from '../../../utils/map-utils';
declare var google;
declare var CustomMarker;
@IonicPage()
@Component({
selector: 'page-home',
templateUrl: 'home.html',
})
export class HomePage {
static title = 'Home';
homeInstance: any;
gMap: any;
myPos: any = {
}
markers: any[] = [];
placesCombo: any[] = ['Perto de mim', 'Porto Alegre'];
searchQuery: string = '';
searchPlaces = [];
showList = false;
places: Array<Establishment> = [];
placeInfo: Establishment = null;
isCardShown: boolean = false;
starData = [
{
"state": 'empty',
},
{
"state": 'empty',
},
{
"state": 'empty',
},
{
"state": 'empty',
},
{
"state": 'empty',
}
];
constructor(public navCtrl: NavController
, public navParams: NavParams
, public geolocation: Geolocation
, public http: Http
, public alertCtrl: AlertController
, public nav: LaunchNavigator
, public diag: Diagnostic
, public actionSheetCtrl: ActionSheetController
, public storage: Storage
, public keyboard: Keyboard) {
this.homeInstance = navParams.data;
this.homeInstance.tabHome = this;
}
ionViewWillLoad() {
this.geolocation.getCurrentPosition().then((resp) => {
this.myPos.lat = resp.coords.latitude;
this.myPos.lng = resp.coords.longitude;
const position = new google.maps.LatLng(this.myPos.lat, this.myPos.lng);
this.gMap.setCenter(position);
var contentString = '<div id="content">' +
'Você está aqui' +
'</div>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var overlay = new CustomMarker(
position,
this.gMap,
{
//------------Here I create the marker for the user's position. Oddly, if I click at this marker first, the other ones seem to work ok after that.
listener: function () {
infowindow.open(this.gMap, overlay);
},
image: "http://www.stickpng.com/assets/images/585e4bf3cb11b227491c339a.png"
}
);
}).catch((error) => {
console.log('Error getting location', error);
});
this.populateLists();
}
ionViewDidLoad() {
const position = new google.maps.LatLng(0, 0);
const mapOptions = {
zoom: 15,
center: position,
disableDefaultUI: true,
zoomControl: true,
scaleControl: true
}
this.gMap = new google.maps.Map(document.getElementById('map'), mapOptions);
this.gMap.setOptions({
styles: [
{
"featureType": "poi",
"elementType": "labels.icon",
"stylers": [
{
"visibility": "off"
}
]
}
]
});
}
populateLists() {
this.http.get(GlobalVars.serverRoot + 'establishments/summary'
).map(res => res.json()).toPromise().then(
data => {
this.places = new Array<Establishment>();
for (let i = 0; i < data.length; i++) {
let establishment = new Establishment(data[i]);
this.places.push(establishment);
let position = new google.maps.LatLng(establishment.location.cordinates.split(', ')[0], establishment.location.cordinates.split(', ')[1]);
const home = this;
var overlay = new CustomMarker(
position,
this.gMap,
{
listener: function () {
//----Here I send the code block I want to run after the marker is clicked.
home.onMarkerClick(establishment);
},
image: establishment.images[0].path_miniature
}
);
this.markers.push(overlay);
};
this.diag.isLocationEnabled().then((state) => {
if (!state) {
let alert = this.alertCtrl.create({
title: "Atenção",
message: "Para obter uma experiência mais personalizada, ative os serviços de localização. Deseja fazer isso agora?",
enableBackdropDismiss: false,
buttons: [{
text: 'Não', handler: (data) => {
alert.dismiss();
}
}, {
text: 'Sim', handler: (data) => {
this.diag.switchToLocationSettings();
}
}]
});
alert.present();
}
});
},
error => {
AlertUtils.showErrorWithHandler(error, this.alertCtrl, (data) => {
this.diag.isLocationEnabled().then((state) => {
if (!state) {
let alert = this.alertCtrl.create({
title: "Atenção",
message: "Para obter uma experiência mais personalizada, ative os serviços de localização. Deseja fazer isso agora?",
enableBackdropDismiss: false,
buttons: [{
text: 'Não', handler: (data) => {
alert.dismiss();
}
}, {
text: 'Sim', handler: (data) => {
this.diag.switchToLocationSettings();
}
}]
});
alert.present();
}
});
});
});
}
getLat(place: any) {
return Number(place.location.cordinates.split(', ')[0]);
}
getLng(place: any) {
return Number(place.location.cordinates.split(', ')[1]);
}
//-----------------THIS is the function called when a marker is clicked, and changes the variables that govern my bottom card behavior. It is called immediately after the click on the marker, but its effects' visibility are delayed
onMarkerClick(place) {
this.placeInfo = place;
this.onStarClass(this.placeInfo.rank);
let home = this;
setTimeout(function () {
home.isCardShown = true;
}, 100);
}
dismissCard(home: HomePage) {
this.isCardShown = false;
setTimeout(function () {
home.placeInfo = null;
}, 400);
}
onCancel($event) { }
closeKeyboard() {
this.keyboard.close();
}
onSearchbarInput(ev: any) {
// set val to the value of the searchbar
let val = ev.target.value;
this.searchQuery = val;
this.getItems(ev);
}
getItems(ev: any) {
// Reset items back to all of the items
this.searchPlaces = [];
// set val to the value of the searchbar
let val = ev.target.value;
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
let qp = new URLSearchParams();
if (this.searchQuery != null && this.searchQuery.trim() != '') {
qp.append('name', this.searchQuery);
}
this.http.get(GlobalVars.serverRoot + 'establishments/summary', { params: qp }).toPromise().then(
response => {
let places = response.json();
for (let i = 0; i < places.length; i++) {
this.searchPlaces.push(new Establishment(places[i]));
}
});
// Show the results
this.showList = true;
} else {
// hide the results when the query is empty
this.showList = false;
}
}
centerPlace(place: Establishment) {
this.searchQuery = place.name;
this.showList = false;
this.searchPlaces = [];
}
checkOffers() {
NavUtils.goToPage(EstablishmentOffersPage, { establishment: this.placeInfo });
}
checkDetails() {
NavUtils.goToPage(EstablishmentDetailsPage, { establishment: this.placeInfo });
}
navigate() {
MapUtils.navigate(this.placeInfo, this.actionSheetCtrl, this.alertCtrl);
}
getStyle() {
if (this.placeInfo != null) {
return { 'background-image': 'url(' + this.placeInfo.images[0].path + ')' };
} else return '';
}
getName() {
if (this.placeInfo != null) {
return this.placeInfo.name;
} else return '';
}
getRate() {
if (this.placeInfo != null) {
return this.placeInfo.rank;
} else return '';
}
onStarClass(rate: number) {
let decimal = parseInt(rate.toPrecision(2).slice(2));
let litStars = parseInt((rate).toPrecision(2).charAt(0)) + (decimal >= 7 ? 1 : 0);
let hasHalfLitStar = decimal >= 2 && decimal < 7;
for (let i = 1; i <= this.starData.length; i++) {
if (i <= litStars) {
this.starData[i - 1].state = 'full';
} else if (i == litStars + 1 && hasHalfLitStar) {
if (hasHalfLitStar) {
this.starData[i - 1].state = 'half'
}
}
}
};
}
Это пользовательский файл маркера js, который я нашел в Интернете в большом руководстве. Я отредактировал его так, чтобы он отображал маркеры так, как я хотел, и чтобы его распознавали классы TS.
var CustomMarker = function (latlng, map, args) {
this.latlng = latlng;
this.args = args;
this.setMap(map);
}
CustomMarker.prototype = new google.maps.OverlayView();
CustomMarker.prototype.draw = function () {
var self = this;
var div = this.div;
if (!div) {
div = this.div = document.createElement('div');
div.className = 'marker';
div.style.position = 'absolute';
div.style.cursor = 'pointer';
div.style.width = '36px';
div.style.height = '46px';
div.style.backgroundImage = "url('https://s3-sa-east-1.amazonaws.com/clubi-app/pin.png')";
div.style.backgroundPosition = "center";
div.style.backgroundSize = "36px 46px";
imgCropper = document.createElement('div');
imgCropper.style.position = 'absolute';
imgCropper.style.zIndex = '-1';
imgCropper.style.cursor = 'pointer';
imgCropper.style.width = '34px';
imgCropper.style.height = '34px';
imgCropper.style.margin = '1px';
imgCropper.style.overflow = 'hidden';
imgCropper.style.borderRadius = '50%';
imgDiv = document.createElement('div');
imgDiv.style.position = 'absolute';
imgDiv.style.cursor = 'pointer';
imgDiv.style.width = '34px';
imgDiv.style.height = '34px';
if (typeof (self.args.image) !== 'undefined') {
imgDiv.style.backgroundImage = "url('" + this.args.image + "')";
imgDiv.style.backgroundPosition = "center";
imgDiv.style.backgroundSize = "34px 34px";
}
imgCropper.appendChild(imgDiv);
div.appendChild(imgCropper);
if (typeof (self.args.marker_id) !== 'undefined') {
div.dataset.marker_id = self.args.marker_id;
}
if (typeof (self.args.listener) !== 'undefined') {
google.maps.event.addDomListener(div, "click", self.args.listener);
}
var panes = this.getPanes();
panes.overlayImage.appendChild(div);
}
var point = this.getProjection().fromLatLngToDivPixel(this.latlng);
if (point) {
div.style.left = (point.x - 18) + 'px';
div.style.top = (point.y - 46) + 'px';
}
};
CustomMarker.prototype.remove = function () {
if (this.div) {
this.div.parentNode.removeChild(this.div);
this.div = null;
}
};
CustomMarker.prototype.getPosition = function () {
return this.latlng;
};
Если есть что-то еще, что я могу предоставить, чтобы помочь определить причину этой проблемы, пожалуйста, дайте мне знать.