В Ionic v3, почему ngClass и ngIf не отражают изменения немедленно из прослушивателя кликов маркера карт Google? - PullRequest
0 голосов
/ 14 сентября 2018

Я создаю приложение с картой на главном экране. Всякий раз, когда пользователь нажимает на маркер, он должен открывать 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;
};

Если есть что-то еще, что я могу предоставить, чтобы помочь определить причину этой проблемы, пожалуйста, дайте мне знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...