Angular 2+ 和实施传单地图(开放街道地图) [英] Angular 2+ and implementing Leaflet maps (Open Street Map)

查看:15
本文介绍了Angular 2+ 和实施传单地图(开放街道地图)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我的网页上实现(ASP.NET Core SPA 模板(使用 Angular 2+ - 使用 yeoman 生成)传单地图.

所以我在leafletjs.com上搜索教程

首先测试了这个例子(它可以独立运行):

<html><头><link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css"完整性="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ=="crossorigin=""/><script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"完整性="sha512-A7vV8IFfih/D732iSSKi20u/ooOfj/AGehOKq0f4vLT1Zr2Y+RX7C+w8A1gaSasGtRUZpF/NZgzSAu4/Gc41Lg=="crossorigin=""></script><风格>#map { 高度:180px;}</风格></头><身体><div id="map"></div><脚本>var map = L.map('map').setView([50.0817444,19.9253805], 13);L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {归属:'&copy;<a href="http://osm.org/copyright">OpenStreetMap</a>贡献者}).addTo(地图);L.marker([50.0817444,19.9253805]).addTo(map).bindPopup('一个漂亮的 CSS3 弹出窗口.<br> 易于定制.').openPopup();</脚本></身体></html>

我尝试将它与 Angular 2+ 一起使用并创建一些简单的组件,例如 LeafletMapComponent. 我已经完成了以下步骤:

  1. 将依赖项添加到 package.json

<块引用>

依赖":{..."传单":"^1.0.3"...}开发依赖":{..."@types/geojson":"^1.0.2","@types/传单":"^1.0.60"}

  1. webpack.config.vendor.js
  2. 中添加传单

<块引用>

条目:{小贩: ['@angular/common','@angular/编译器','@角/核心','@angular/http','@angular/平台浏览器','@angular/平台浏览器动态','@angular/路由器','@angular/平台服务器','angular2-universal','angular2-universal-polyfills','引导','bootstrap/dist/css/bootstrap.css','es6-垫片','es6-promise','事件源-polyfill','jquery','zone.js','传单',]},输出: {publicPath: '/dist/',文件名:'[名称].js',库:'[名称]_[哈希]'},

  1. 然后我用这么简单的代码创建了新组件

<块引用>

import {Component, OnInit} from '@angular/core';从'传单'导入*作为L;@零件({选择器:'传单地图',templateUrl: 'leaflet-map.component.html',styleUrls: ['leaflet-map.component.css', '../../../..//node_modules/leaflet/dist/leaflet.css'], }) 导出类LeafletMapComponent 实现 OnInit {ngAfterViewInit() {L.map('leafletMap').setView([50.0817444,19.9253805], 13);}ngOnInit() {}}

  1. 当然我也应用了#leafletMap div 和样式

<块引用>

 #leafletMap {高度:400px;宽度:600px;}<div id="leafletMap"></div>

  1. 我运行过这样的应用程序:

<块引用>

 $ npm install$ rimraf dist &&./node_modules/.bin/webpack --configwebpack.config.vendor.js &&./node_modules/.bin/webpack --configwebpack.config.js$点网运行

但我仍然收到这样的错误:

<块引用>

失败:Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0]发生未处理的异常:调用节点模块失败并出现错误:预渲染因错误而失败:ReferenceError:窗口未定义

解决方案

您似乎在 ASP.NET Core Angular 启动器中使用了 Angular Universal(服务器端渲染).在服务器端没有定义窗口对象(LeafletMap 可能在内部使用).

有两种选择:

  1. 通过从 Views/Home/Index.cshtml 中的元素中删除 asp-prerender-module 属性来禁用项目中的服务器端呈现.
  2. 通过在您的代码中实现一个专用的执行分支,将 LeafletMap 的使用限制在客户端部分(请参阅 https://github.com/angular/universal#universal-gotchas)

编辑

根据选项 2,您必须注意在加载第三方库(如 jquery 或 LeafletMap)时没有万能药.只是 import 语句本身(如果没有在代码优化中删除)可能会在服务器端引起奇怪的副作用.可能"的做法(因为没有最佳"做法)可以实现有条件地加载这些库的服务.

import { Injectable, Inject, PLATFORM_ID, Component, OnInit } from '@angular/core';从@angular/common"导入 { isPlatformBrowser, isPlatformServer };@Injectable()导出类 LegacyService {私人_L:任何;私人_jquery:任何;公共构造函数(@Inject(PLATFORM_ID)私有_platformId:对象){this._init();}公共 getJquery() {返回 this._safeGet(() => this._jquery);}公共getL(){返回 this._safeGet(() => this._L);}私人 _init() {如果(isPlatformBrowser(this._platformId)){this._requireLegacyResources();}}私人 _requireLegacyResources() {this._jquery = require('jquery');this._L = require('传单');}私人 _safeGet(getCallcack : () => any) {如果(isPlatformServer(this._platformId)){抛出新错误('对服务器上旧组件的无效访问');}返回 getCallcack();}}

在组件实现中,您还应该实现仅在客户端使用服务的条件分支:

ngAfterViewInit() {如果(isPlatformBrowser(this.platformId)){this._legacyService.getL().map('leafletMap').setView([50.0817444,19.9253805], 13);}}

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/leaflet@1.0.3/dist/leaflet.css"
   integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ=="
   crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.0.3/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: '&copy; <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:

  1. add dependencies to package.json

"dependencies": {
    ...
   "leaflet":"^1.0.3"
    ...
}
"devDependencies": {
    ...
    "@types/geojson":"^1.0.2",
    "@types/leaflet":"^1.0.60"
}

  1. add leaflet in webpack.config.vendor.js

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]'
        },

  1. Then I have created new component with such simple code

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() {  

    }    
  }

  1. of course I also have #leafletMap div and style applied

 #leafletMap { 
     height: 400px; 
     width: 600px;
  }

  <div id="leafletMap"></div>

  1. I have run app like this:

  $ 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

解决方案

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:

  1. Disable server side rendering in your project by removing asp-prerender-module attribute from the element in Views/Home/Index.cshtml.
  2. Limit the use of LeafletMap to the client side part by implementing a dedicated execution branch in your code (see https://github.com/angular/universal#universal-gotchas)

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

这篇关于Angular 2+ 和实施传单地图(开放街道地图)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆