Reputation: 11752
I would like to implement on my web page (ASP.NET Core SPA template (with Angular 2+ - generated with yeoman) Leaflet maps.
So I search for tutorial on leafletjs.com
Firstly tested this example (and it works as standalone):
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ=="
crossorigin=""/>
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"
integrity="sha512-A7vV8IFfih/D732iSSKi20u/ooOfj/AGehOKq0f4vLT1Zr2Y+RX7C+w8A1gaSasGtRUZpF/NZgzSAu4/Gc41Lg=="
crossorigin=""></script>
<style>
#map { height: 180px; }
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = L.map('map').setView([50.0817444,19.9253805], 13);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
L.marker([50.0817444,19.9253805]).addTo(map)
.bindPopup('A pretty CSS3 popup.<br> Easily customizable.')
.openPopup();
</script>
</body>
</html>
Than I have tried to use it with Angular 2+ and create some simple component like LeafletMapComponent.
I have done below steps:
"dependencies": { ... "leaflet":"^1.0.3" ... } "devDependencies": { ... "@types/geojson":"^1.0.2", "@types/leaflet":"^1.0.60" }
entry: { vendor: [ '@angular/common', '@angular/compiler', '@angular/core', '@angular/http', '@angular/platform-browser', '@angular/platform-browser-dynamic', '@angular/router', '@angular/platform-server', 'angular2-universal', 'angular2-universal-polyfills', 'bootstrap', 'bootstrap/dist/css/bootstrap.css', 'es6-shim', 'es6-promise', 'event-source-polyfill', 'jquery', 'zone.js', 'leaflet', ] }, output: { publicPath: '/dist/', filename: '[name].js', library: '[name]_[hash]' },
import {Component, OnInit} from '@angular/core'; import * as L from 'leaflet'; @Component({ selector: 'leaflet-map', templateUrl: 'leaflet-map.component.html', styleUrls: ['leaflet-map.component.css', '../../../..//node_modules/leaflet/dist/leaflet.css'], }) export class LeafletMapComponent implements OnInit { ngAfterViewInit() { L.map('leafletMap').setView([50.0817444,19.9253805], 13); } ngOnInit() { } }
#leafletMap { height: 400px; width: 600px; } <div id="leafletMap"></div>
$ npm install $ rimraf dist && ./node_modules/.bin/webpack --config webpack.config.vendor.js && ./node_modules/.bin/webpack --config webpack.config.js $ dot net run
But I am still getting an error like this:
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0] An unhandled exception has occurred: Call to Node module failed with error: Prerendering failed because of error : ReferenceError: window is not defined
Upvotes: 1
Views: 6144
Reputation: 121
It seems like you are using Angular Universal (server side rendering) in your ASP.NET Core Angular starter. On server side there is no window object defined (that LeafletMap might use internally).
There are two options:
asp-prerender-module
attribute from the element in Views/Home/Index.cshtml.EDIT
According to option 2 you must note that there is no panacea on that when loading third party libraries like jquery or LeafletMap. Just the import statement itself (if not removed on code optimization) can cause strange side effects on server side. A "possible" practice (since there is no "best" practice) can be to implement a service that conditionally loads those libraries.
import { Injectable, Inject, PLATFORM_ID, Component, OnInit } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
@Injectable()
export class LegacyService {
private _L : any;
private _jquery : any;
public constructor(@Inject(PLATFORM_ID) private _platformId: Object) {
this._init();
}
public getJquery() {
return this._safeGet(() => this._jquery);
}
public getL() {
return this._safeGet(() => this._L);
}
private _init() {
if(isPlatformBrowser(this._platformId)){
this._requireLegacyResources();
}
}
private _requireLegacyResources() {
this._jquery = require('jquery');
this._L = require('leaflet');
}
private _safeGet(getCallcack : () => any) {
if(isPlatformServer(this._platformId)){
throw new Error ('invalid access to legacy component on server');
}
return getCallcack();
}
}
On component implementation you should also implement conditional branches that only use the service on client side:
ngAfterViewInit() {
if(isPlatformBrowser(this.platformId)){
this._legacyService.getL().map('leafletMap').setView([50.0817444,19.9253805], 13);
}
}
Upvotes: 4