ArcGIS API for JavaScript 4.18.1 - Angular 11, Typescript, NPM - javascript

I am looking at ArcGIS Javascript API 4.18.1. But it is confusing, how I should add it to a new Angular 11 project. Is there an example project somewhere that shows the basic folder structure and getting a map setup in Angular 11? I want to get it set up with just ES modules using NPM.
https://developers.arcgis.com/javascript/latest/es-modules/
I did this:
npm install #arcgis/core
Then I added the following to app.component.ts
import WebMap from '#arcgis/core/WebMap';
import MapView from '#arcgis/core/views/MapView';
I am new to this. And It seems like all the documentation talks about React.
Then in the getting started with Map it has you enter a key:
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>ArcGIS API for JavaScript Tutorials: Display a map</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.18/esri/themes/light/main.css">
<script src="https://js.arcgis.com/4.18/"></script>
<script>
require(["esri/config","esri/Map", "esri/views/MapView"], function (esriConfig,Map, MapView) {
esriConfig.apiKey = "YOUR-API-KEY";
const map = new Map({
basemap: "arcgis-topographic" // Basemap layer service
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
But these docs are not for Typescript. Do they have typescript docs somewhere? How do you add the Key with the new API 4.18.1 and Typescript and NPM and Angular 11?

This is what I came up with.
It is based off https://github.com/TheKeithStewart/angular-esri-components which uses the arcgis-js-api library
https://github.com/Esri/jsapi-resources/tree/master/esm-samples/jsapi-angular-cli has the basic setup instructions for using #arcgis/core in an Angular application
esri-map.component.scss:
#mapViewNode {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
esri-map.component.ts:
import {
Component,
ElementRef,
Input,
OnInit,
NgZone,
OnDestroy,
ViewChild,
Output,
EventEmitter,
} from '#angular/core';
import config from '#arcgis/core/config';
import { EsriMapService } from './esri-map.service';
#Component({
// tslint:disable-next-line: component-selector
selector: 'esri-map',
template: '<div #mapViewNode></div>',
styleUrls: ['./esri-map.component.scss'],
})
export class EsriMapComponent implements OnInit, OnDestroy {
#ViewChild('mapViewNode', { static: true }) private elementRef!: ElementRef;
#Input() mapProperties!: any;
#Input() mapViewProperties!: any;
#Output() mapInit: EventEmitter<any> = new EventEmitter();
private mapView: any;
constructor(private zone: NgZone, private mapService: EsriMapService) {}
ngOnInit(): void {
config.assetsPath = 'assets/';
this.zone.runOutsideAngular(() => {
this.loadMap();
});
}
ngOnDestroy(): void {
this.mapView.destroy();
}
loadMap(): void {
this.mapService.isLoaded.subscribe((n: any) => {
this.mapView = n.view;
this.zone.run(() => {
this.mapInit.emit({ map: n.map, view: n.view });
this.mapInit.complete();
});
});
this.mapService.loadWebMap({
...this.mapProperties,
...this.mapViewProperties,
container: this.elementRef.nativeElement,
});
}
esri-map.service.ts:
import { EventEmitter, Injectable } from '#angular/core';
import MapView from '#arcgis/core/views/MapView';
import WebMap from '#arcgis/core/WebMap';
import Widget from '#arcgis/core/widgets/Widget';
import Layer from '#arcgis/core/layers/Layer';
export type Position =
| 'bottom-leading'
| 'bottom-left'
| 'bottom-right'
| 'bottom-trailing'
| 'top-leading'
| 'top-left'
| 'top-right'
| 'top-trailing'
| 'manual';
#Injectable({
providedIn: 'root',
})
export class EsriMapService {
map!: WebMap;
view!: MapView;
loaded = false;
isLoaded = new EventEmitter();
constructor() {}
loadWebMap(props: {
basemap: any;
container: any;
center: any;
zoom: any;
}): void {
this.map = new WebMap({ basemap: props.basemap });
this.view = new MapView({
container: props.container,
map: this.map,
center: props.center,
zoom: props.zoom,
});
this.loaded = true;
this.isLoaded.emit({
map: this.map,
view: this.view,
});
}
addLayer(layer: Layer, clearLayers?: boolean): void {
if (clearLayers) {
this.view.map.removeAll();
}
this.view.map.add(layer);
}
addWidget(
component: string | HTMLElement | Widget | any[],
position?: Position,
): void {
this.view.ui.add(component, position);
}
}
You would just instantiate the component like this:
<esri-map [mapProperties]="mapProperties" [mapViewProperties]="mapViewProperties" (mapInit)="onMapInit($event)">
</esri-map>
mapProperties sets the base map.
mapViewProperties sets the center and zoom.
mapInit lets you know when the map is loaded.
The service provides the map view for constructing the map.
You would then add your layers to the map view as usual.

Related

Leaflet marker not appearing when using observables

map.component.ts
export class MapComponent implements AfterViewInit {
private map;
private centroid: L.LatLngExpression = [49.2827, -123.1207];
private initMap(): void {
this.map = L.map('map', {
center: this.centroid,
zoom: 12
});
const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
minZoom: 3,
attribution: '© OpenStreetMap'
});
tiles.addTo(this.map);
}
constructor(private _dataService: DataService) {
}
ngAfterViewInit(): void {
this.initMap();
this._dataService.pigMessage$.subscribe(message => {
L.marker([49.2827, -123.1207]).addTo(this.map).bindPopup("test").openPopup();
console.log(message);
})
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
import { pigReportType } from './pigReport';
#Injectable({
providedIn: 'root'
})
export class DataService {
private _pigMessageSource = new Subject<pigReportType>();
pigMessage$ = this._pigMessageSource.asObservable();
sendPigData(message: pigReportType){
this._pigMessageSource.next(message)
}
constructor() { }
}
When I click on the submit button, data service sends a new piece of information into the observable and redirects back to the map component. The problem is the marker is not adding inside of the subscribe function in map.component.ts.
Additionally, I have checked that the .subscribe function works because it prints the correct message. The markers just do not appear on the map.
I have tried looking through the html and seeing if there is a duplicated map that is covering the map with the marker but there isn't. Also I have tried to call initMap inside of the .subscribe function but it doesn't work.
I'm hoping if someone can point me in the right direction because I have searched everywhere on the web for a solution but can't find it.

HERE maps in angular show a blank map

even when I use the exact example of here map official site I received a blank view, only with HERE logo and the rest of this layer.
I received a "WebGL warning: uniform setter: No active linked Program." warning on my firefox and VSC debug console.
I changed the navigator to Chrome and Opera, and I received the same result.
My apikey is alright, I test it on postman and it worked.
This my code
import { Component, ElementRef, OnInit, ViewChild, Input } from '#angular/core';
declare var H: any;
#Component({
selector: 'here-map',
templateUrl: './heremap.component.html',
styleUrls: ['./heremap.component.scss'],
})
export class HeremapComponent implements OnInit {
#ViewChild("map") public mapElement: ElementRef;
#Input() private _apikey: any;
#Input() public lat: any;
#Input() public lng: any;
#Input() public width: any;
#Input() public height: any;
private map: any;
constructor() { }
ngOnInit() {}
public ngAfterViewInit() {
console.log('HERE apikey: ', this._apikey);
let platform = new H.service.Platform({
apikey: this._apikey
});
let defaultLayers = platform.createDefaultLayers();
this.map = new H.Map(this.mapElement.nativeElement,
defaultLayers.vector.normal.map,
{
zoom: 10,
center: { lat: this.lat, lng: this.lng }
}
);
this.map.setCenter({lat: 52.5159, lng: 13.3777});
this.map.setZoom(14);
}
}
<div class="here-maps" #map [style.width]="width" [style.height]="height"></div>
I was having the same issue, and I fixed it by resizing the map after it was fully loaded.
You must trigger the resize event after 500ms (or something like that) at the end of the ngAfterViewInit() event:
This is an example of my code:
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
}, 500);
In the resize event, you should resize the map:
#HostListener('window:resize', ['$event'])
onResize(event: any) {
requestAnimationFrame(() => this.map.getViewPort().resize());
}
After this, all is working as expected:

Ionic 3 Google Maps Integration

I am following this tutorial exactly: https://www.djamware.com/post/58f4da2080aca7414e78a638/step-by-step-tutorial-of-ionic-3-angular-4-and-google-maps-directions-service
But I cannot get it to work. I have the API key set up no problem, but for some reason I keep getting the error Error: Uncaught (in promise): ReferenceError: google is not defined
ReferenceError: google is not defined
I am running the app using ionic lab
For some reason it isn't working. Can someone help me find the problem? I have tried adding the cordova whitelist plugin, changing the https to http in the API key part, but still it isn't working.
Did you declare the variable at home.ts?
import { Component, ViewChild, ElementRef } from '#angular/core';
import { IonicPage } from 'ionic-angular';
import { NavController } from 'ionic-angular';
declare var google;
#IonicPage()
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
...
Ionic 3 Google Map click doc link
1.install this package
$ ionic cordova plugin add https://github.com/mapsplugin/cordova-plugin-googlemaps#multiple_maps --variable API_KEY_FOR_ANDROID="YOUR_ANDROID_API_KEY_IS_HERE" --variable API_KEY_FOR_IOS="YOUR_IOS_API_KEY_IS_HERE"
$ npm install --save #ionic-native/google-maps
2.Android and ios Api create
Go to this site - google cloud platform
3.After you got Api install packages
4.import
app.module.ts
import { GoogleMaps } from '#ionic-native/google-maps';
......
provider:[
GoogleMaps
];
5.home.html
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<div id="map"></div>
</ion-content>
6.home.ts
import { Component } from '#angular/core';
import { NavController, Platform } from 'ionic-angular';
import {
GoogleMaps,
GoogleMap,
GoogleMapsEvent,
LatLng,
CameraPosition,
MarkerOptions,
Marker
} from '#ionic-native/google-maps';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController,
private googleMaps: GoogleMaps,
public platform: Platform) {
platform.ready().then(()=>{
this.loadMap();
})
}
loadMap() {
// create a new map by passing HTMLElement
let element: HTMLElement = document.getElementById('map');
let map: GoogleMap = this.googleMaps.create(element);
// listen to MAP_READY event
// You must wait for this event to fire before adding something to the map or modifying it in anyway
map.one(GoogleMapsEvent.MAP_READY).then(
() => {
console.log('Map is ready!');
// Now you can add elements to the map like the marker
}
);
// create CameraPosition
let position: CameraPosition = {
target: {
lat: 43.0741904,
lng: -89.3809802
},
zoom: 18,
tilt: 30
};
// move the map's camera to position
map.moveCamera(position);
// create new marker
let markerOptions: MarkerOptions = {
//position: ionic,
title: 'Ionic'
};
map.addMarker(markerOptions)
.then((marker: Marker) => {
marker.showInfoWindow();
});
}
}

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);
});

Angular 2-RC4 Dynamic Content Loading

Currently, I'm trying incorporating Leaflet into Angular 2-RC4. I have faced with the following problem of dynamically loading html into popupContent of leaflet markers.
In the parent class I have the following code:
export class BeaconLayerComponent implements OnInit {
constructor(private resolver: ComponentResolver, private viewRef: ViewContainerRef) {}
createMultipleDynamicInstance(markers) {
markers.foreach((marker) => {
createDynamicInstance(marker);
}
}
createDynamicInstance() {
this.resolver.resolveComponent(BeaconPopupComponent).then((factory:ComponentFactory<BeaconPopupComponent>) => {
let popupComponentRef: ComponentRef<BeaconPopupComponent>;
popupComponentRef = this.viewRef.createComponent(factory);
popupComponentRef.instance.name = beaconJSON.name;
popupComponentRef.instance.afterViewCheckedEventEmitter.subscribe((popupInnerHTML: string) => {
//make use of the innerHTML
// ... <= Create the leaflet marker with innerHTML as popupContent
//After retrieving the HTML, destroy the element
popupComponentRef.destroy();
})
});
Child component:
import {Component, AfterContentInit, EventEmitter, AfterViewInit, AfterViewChecked, ElementRef} from "#angular/core";
#Component({
selector: 'beaconPopup',
template: `
<div>{{name}}</div>
`
})
export class BeaconPopupComponent implements AfterViewChecked {
constructor(private elementRef: ElementRef) {}
name:string;
public afterViewCheckedEventEmitter = new EventEmitter();
ngAfterViewChecked() {
console.log("ngAfterViewChecked()");
this.afterViewCheckedEventEmitter.emit(this.elementRef.nativeElement.innerHTML);
}
}
When I run the html I get these errors:
2016-07-19 00:02:29.375 platform-browser.umd.js:1900 Error: Expression has changed after it was checked. Previous value: '[object Object]'. Current value: 'Happening Beacon'
at ExpressionChangedAfterItHasBeenCheckedException.BaseException [as constructor]
I'm trying to avoid getting the DOM element via JQuery
getDynamicElement(name)
{
str = "<div><div>" + name + "<div></div>"
return $(str).get[0]
}
Is there a better way to do it in Angular 2?
These are two tricks inside your code, 1 dynamical load, 2 how to use jQuery($)
Here is how I create dynamica component from string
compileToComponent(template1: string): Promise<ComponentFactory<any>> {
const metadata = new ComponentMetadata({
template: template1,
directives: ROUTER_DIRECTIVES
});
let decoratedCmp = Component(metadata)(class DynamicComponent { });
return this.resolver.resolveComponent(decoratedCmp);
}
For more details check here
https://github.com/Longfld/DynamicRouter_rc4/blob/master/app/MyRouterLink.ts
or demo here http://plnkr.co/edit/diZgQ8wttafb4xE6ZUFv?p=preview
For rc5 see here :http://plnkr.co/edit/nm5m7N?p=preview

Categories