Fix scopes in Angular component - javascript

I'm using 2GIS maps API based on Leaflet library. There are few functions in MapService (such as addMarkerToList()) which need to be called while clicking on map.
But there is a problem: using this.mapService.addMarkerToList will caught an error:
ERROR TypeError: Cannot read property 'addMarkerToList' of undefined
So I use const self = this construction to avoid this problem.
Can it be solved by another way?
map.component.ts
import {Component, OnInit} from '#angular/core';
import * as DG from '2gis-maps';
import {MapService} from '../../services/map/map.service';
let currentUserPos;
#Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
constructor(private mapService: MapService) {}
ngOnInit() {
const self = this;
const map = DG.map('map', {
'center': [54.98, 82.89],
'zoom': 13,
'fullscreenControl': false
});
if (map) {
console.log('Map added');
}
map.locate()
.on('locationfound', function (e) {
currentUserPos = DG.marker([e.latitude, e.longitude]).addTo(map).bindLabel('Your location', {
static: true
});
const currentCenter = new DG.latLng(e.latitude, e.longitude);
console.log('Current position founded: ' + 'lat = ' + e.latitude + '; lng = ' + e.longitude);
map.setView(currentCenter, 16);
});
map.on('click', function (e) {
const marker = DG.marker([e.latlng.lat, e.latlng.lng]);
marker.addTo(map);
marker.on('click', function() {
marker.remove(map);
self.mapService.deleteMarkerFromList(e.latlng.lat, e.latlng.lng);
});
console.log('Marker after adding:' + marker.getLatLng());
self.mapService.addMarkerToList(e.latlng.lat, e.latlng.lng);
});
}
}

Related

How to fix Ionic NullInjectorError while using the google maps API with a toggle button?

I have a project where I use the Google Maps API with Ionic and I want to create a toggle where you can switch between satellite view and street view with the toggle on and off. This is something I've been stuck on for quite some time and I believe this issue is caused by the way I'm importing the SearchPage and using map, but any feedback would be appreciated.
Error Message:
VM67048 vendor.js:62134 ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(SearchPageModule)[SearchPage -> SearchPage -> SearchPage -> SearchPage]:
NullInjectorError: No provider for SearchPage!
NullInjectorError: R3InjectorError(SearchPageModule)[SearchPage -> SearchPage -> SearchPage -> SearchPage]:
NullInjectorError: No provider for SearchPage!
at NullInjector.get (VM58879 vendor.js:67026)
at R3Injector.get (VM58879 vendor.js:67193)
at R3Injector.get (VM58879 vendor.js:67193)
at R3Injector.get (VM58879 vendor.js:67193)
at NgModuleRef$1.get (VM58879 vendor.js:81247)
at R3Injector.get (VM58879 vendor.js:67193)
at NgModuleRef$1.get (VM58879 vendor.js:81247)
at Object.get (VM58879 vendor.js:80961)
at lookupTokenUsingModuleInjector (VM58879 vendor.js:59313)
at getOrCreateInjectable (VM58879 vendor.js:59425)
at resolvePromise (VM58874 polyfills.js:3445)
at VM58874 polyfills.js:3352
at rejected (VM58879 vendor.js:100902)
at ZoneDelegate.invoke (VM58874 polyfills.js:3011)
at Object.onInvoke (VM58879 vendor.js:84501)
at ZoneDelegate.invoke (VM58874 polyfills.js:3010)
at Zone.run (VM58874 polyfills.js:2770)
at VM58874 polyfills.js:3504
at ZoneDelegate.invokeTask (VM58874 polyfills.js:3046)
at Object.onInvokeTask (VM58879 vendor.js:84488)
For example, here is my HTLM code for the toggle button (which is inside a fab button):
<ion-list>
<ion-item><ion-text style="color: blue;">Satellite View</ion-text><ion-toggle (ionChange)="update($event)" color="primary" md="md"></ion-toggle></ion-item>
</ion-list>
Here is the ts code for the update() function:
import { Component, OnInit } from '#angular/core';
import {SearchPage} from "../search/search.page"
#Component({
selector: 'app-accessibility-popover',
templateUrl: './accessibility-popover.component.html',
styleUrls: ['./accessibility-popover.component.scss'],
})
export class AccessibilityPopoverComponent implements OnInit {
constructor(private searchpage: SearchPage) { }
ngOnInit() {}
update(event)
{
if (event.detail.checked == true)
{
searchpage.Test.map.setMapTypeId('terrain');
}
console.log(event.detail.checked);
}
}
here is the code for the searchpage, which actually has all the code for the api and displays the map:
import { Component, OnInit, ViewChild, ElementRef } from '#angular/core';
declare var google: any;
#Component({
selector: 'app-search',
templateUrl: './search.page.html',
styleUrls: ['./search.page.scss'],
})
export class SearchPage implements OnInit {
map: any;
#ViewChild('map', {read: ElementRef, static: false}) mapRef: ElementRef;
infoWindows: any = [];
markers: any = [
{
title: "Building1",
latitude: "33.646479464261766",
longitude: "-117.84011195587281"
}
];
constructor() { }
ngOnInit() {
}
ionViewDidEnter() {
this.showMap();
}
addMarkersToMap(markers) {
for (let marker of markers) {
let position = new google.maps.LatLng(marker.latitude, marker.longitude);
let mapMarker = new google.maps.Marker({
position: position,
title: marker.title,
latitude: marker.latitude,
longitude: marker.longitude
});
mapMarker.setMap(this.map);
this.addInfoWindowToMarker(mapMarker);
}
}
addInfoWindowToMarker(marker) {
let infoWindowContent = '<div id="content">' +
'<h2 id="firstHeading" class"firstHeading">' + marker.title + '</h2>' +
'<p>Latitude: ' + marker.latitude + '</p>' +
'<p>Longitude: ' + marker.longitude + '</p>' +
'</div>';
let infoWindow = new google.maps.infoWindow({
content: infoWindowContent
});
marker.addListener('click', () => {
this.closeAllInfoWindows();
infoWindow.open(this.map, marker);
});
this.infoWindows.push(infoWindow);
}
closeAllInfoWindows() {
for(let window of this.infoWindows) {
window.close();
}
}
showMap() {
const location = new google.maps.LatLng(33.64601498874652, -117.84279416485856);
const options = {
center: location,
zoom: 17,
disableDefaultUI: true,
mapTypeId: 'satellite'
}
this.map = new google.maps.Map(this.mapRef.nativeElement, options);
}
changeMap() {
this.map.setMapTypeId('terrain');
}
}

Angular: Cannot read property 'setLng' of null in component variable

I'm new to angular and would like to ask if why I'm encountering Cannot read property 'setLng' of null?
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
lat: number = 0;
lng: number = 0;
maptitle : string = '';
constructor() {
}
ngOnInit() {
this.initMap();
}
initMap() {
if (navigator) {
navigator.geolocation.watchPosition(this.showPos);
};
}
showPos(position) {
this.setLng(position.coords.latitude);
this.setLat(position.coords.latitude);
}
setLng(lng){
this.lng = lng;
}
setLat(lat){
this.lat = lat;
}
}
Error
ERROR TypeError: Cannot read property 'setLng' of null
at webpackJsonp.../../../../../src/app/map/map.component.ts.MapComponent.showPos (map.component.ts:26)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388)
at Object.onInvoke (core.js:4749)
The issue is only due to scope.
Do Something like this :
initMap() {
if (navigator) {
navigator.geolocation.watchPosition(this.showPos.bind(this));
};
}
The problem is that watchPosition expects a callback and the scope is changed .
By binding this , the scope is always of the class.

Angular 2/4 Bing Map pushpin custom template function call

I have problem to fit my pushpin custom template into angular component.
My component:
import { Component, AfterViewInit } from '#angular/core';
#Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.scss']
})
export class MapComponent implements, AfterViewInit {
#ViewChild('myMap') myMap;
map: any;
infoboxClick;
infoboxTemplate = `<div id="infoboxText" class="custom-infobox">
<span class ="close-sighn" onclick="closeCustomInfoBox()">x</span>
{description}
</div>`;
constructor(private dataService: MapService) {
}
ngAfterViewInit() {
this.getMap();
}
getMap() {
if ((window as any).Microsoft && (window as any).Microsoft.Maps) {
this.map = new (window as any).Microsoft.Maps.Map(document.getElementById('mapId'), {
credentials: ''
});
var pushpin = new (window as any).Microsoft.Maps.Pushpin(map.getCenter(), null);
(window as any).Microsoft.Maps.Events.addHandler(pushpin, 'click', (args) => {
this.infoboxClick.setOptions({
location: args.target.getLocation(),
htmlContent: this.infoboxTemplate.replace('{description}', 'Some test description'),
visible: true
});
});
map.entities.push(pushpin);
} else {
setTimeout(() => { this.getMap() }, 1000);
}
}
closeCustomInfoBox() {
this.infoboxClick.setOptions({
visible: false
});
}
}
My view:
<div #myMap id="mapId" class="map-container"></div>
In infoboxTemplate I have function 'closeCustomInfoBox()', which should close infobox.
1) How I can call that function from my angular component?
2) I need to get proper angular scope if I call it how I can get approach to my 'infoboxClick' variables?
If you mean you want to access closeCustomInfoBox() function and infoboxClick variable from the parent component.
Check this
https://angular.io/guide/component-interaction#parent-interacts-with-child-via-local-variable

Angular 2, child component using with google maps events losing context

I have a case when I am using component in component. By accessing properties of child component via #ViewChild, I am getting undefined for methods in child component.
// Parent component
declare var google: any;
#Component({
selector: 'app-map',
templateUrl: `
<div id="map"></div>
<app-context-menu></app-context-menu>`,
styleUrls: ['./map.component.css'],
providers: [CitiesService],
})
export class MapComponent implements AfterViewInit {
#ViewChild(ContextMenuComponent) contextMenu: ContextMenuComponent;
user: UserInterface;
city: any;
map: any;
constructor(
private userStorage: UserStorage,
private citiesService: CitiesService,
private theMap: TheMap,
) {}
ngAfterViewInit() {
this.findUserCityCoords();
}
inittializeMap(mapOpts) {
let mapProps = {
center: new google.maps.LatLng(mapOpts.lat, mapOpts.lng),
zoom: mapOpts.zoom,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
let map = new google.maps.Map(document.getElementById('map'), mapProps);
let menu = this.contextMenu;
map.addListener('rightclick', (e) => {
console.log('map', this);
console.log('menu', this.contextMenu);
this.contextMenu.open(e);
});
}
findUserCityCoords() {
let userCity = this.userStorage.getFromStorage().city;
this.citiesService.getCityConfig()
.subscribe(cities => {
cities.forEach(city => {
if (city.name === userCity) this.inittializeMap(city.center);
});
});
}
From this class when 'map event listener' calls 'menu.open(e);' the context changes and inside child component, the methods of child component are not available. To call i.e. 'this.draw()' method.
// Child component
import { Component, OnInit, ElementRef, AfterViewInit } from
'#angular/core';
import { TheMap } from '../../services/mapServices/TheMap';
import { Marker } from '../../services/mapServices/marker.service';
declare var google: any;
#Component({
selector: 'app-context-menu',
templateUrl: './context-menu.component.html',
styleUrls: ['./context-menu.component.css']
})
export class ContextMenuComponent {
overlay;
el;
constructor(
private theMap: TheMap,
private marker: Marker,
private elementRef: ElementRef
) {
this.overlay = new google.maps.OverlayView();
}
ngAfterViewInit() {}
open(e) {
let map = this.theMap.getMap();
this.overlay.set('position', e.latLng);
this.overlay.setMap(map);
this.draw(); // <---- Here, this.draw() is not a function
// So I can access properties from constructor but not this.draw() and other methods of this class
};
draw() {
// ....
}
How can I properly implement google's map 'rightclick' event listener (probably with 'bind') to use my child's component methods. Thanks
Not sure I understand the question but () => (arrow function) might be what you're looking for
map.addListener('rightclick', (e) => {
console.log('map', this);
console.log('menu', menu);
this.contextMenu.open(e);
});

Update angular2 View/global variable with data inside a promise

Im using fetch to get data from an external API like this and it is printing to the console correctly. But this.games doesn't update in the global scope so the view isn't updating. How can I update the global gams variable with the data from within the promise once the fetch has returned :
import {bootstrap, Component, CORE_DIRECTIVES, Pipe, Inject} from 'angular2/angular2';
import { Game } from './game/game';
import { API } from './services/API';
import {NgZone} from 'angular2/angular2';
#Component({
selector: 'app',
template: `
Hello
<button (click)="onClickMe()">Click me!</button>
<div *ng-for="#game of games" class = "game">
//layout
</div>
`,
directives: [CORE_DIRECTIVES, Game],
})
export class App {
data: any;
api: API;
games: Game[];
constructor(api:API) {
this.api = api;
this.games = [];
api.fetchGames().then(function(response) {
this.data = response;
console.log(this.data);
var gamesTemp = [];
this.teams = this.data.resultSets[1].rowSet;
for (var i = 0; i < this.teams.length - 1; i += 2) {
//manipulate
}
this.games = gamesTemp;
console.log(this.games);
});
and this it the fetchGames method:
fetchGames() {
return fetch('http://stats.nba.com/stats/scoreboardV2?DayOffset=0&LeagueID=00&gameDate=11%2F5%2F2015')
.then(function(response) {
return response.json();
}).catch(function(ex) {
console.log('parsing failed', ex);
});
}
This seems like a scope (this) problem. Your this inside the callback is not your class! The simplest solution is to use es6 arrow functions:
import {bootstrap, Component, CORE_DIRECTIVES, Pipe, Inject} from 'angular2/angular2';
import { Game } from './game/game';
import { API } from './services/API';
import {NgZone} from 'angular2/angular2';
#Component({
selector: 'app',
template: `
Hello
<button (click)="onClickMe()">Click me!</button>
<div *ng-for="#game of games" class = "game">
//layout
</div>
`,
directives: [CORE_DIRECTIVES, Game],
})
export class App {
data: any;
api: API;
games: Game[];
constructor(api:API) {
this.api = api;
this.games = [];
api.fetchGames().then((response) => { //es6 arrow function was meant to solve this problem!
this.data = response;
console.log(this.data);
var gamesTemp = [];
this.teams = this.data.resultSets[1].rowSet;
for (var i = 0; i < this.teams.length - 1; i += 2) {
//manipulate
}
this.games = gamesTemp;
console.log(this.games);
});

Categories