Как включить ArcGIS Javascript API через CDN с Webpack для работы со Stimulus? - PullRequest
0 голосов
/ 07 октября 2019

В руководстве по быстрому запуску для javascript api ArcGIS приведен следующий пример кода:

<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ArcGIS API for JavaScript Hello World App</title>
  <style>
    html, body, #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

  <link rel="stylesheet" href="https://js.arcgis.com/4.12/esri/css/main.css">
  <script src="https://js.arcgis.com/4.12/"></script>

  <script>
    require([
      "esri/Map",
      "esri/views/MapView"
    ], function(Map, MapView) {

      var map = new Map({
        basemap: "topo-vector"
      });

      var view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-118.71511,34.09042],
        zoom: 11
      });

    });
  </script>
</head>
<body>
  <div id="viewDiv"></div>
</body>
</html>

, который отлично подходит для простой веб-страницы. Однако я использую Blazor (на стороне сервера) и хотел бы инкапсулировать (приведенный выше) код в компонент Blazor. Поэтому я столкнулся с первым камнем преткновения - мне не разрешено добавлять тег <script> внутри компонента Blazor. Это потому, что элемент управления может быть создан динамически в любое время. Поэтому я решил вместо этого решить проблему со Стимулом.

Вот мой компонент Blazor (пока). У меня есть файл с именем Map.razor:

<div data-controller="map"></div>

@code {
    protected override bool ShouldRender()
    {
        var allowRefresh = false;

        return allowRefresh;
    }
}

Я добавил метод ShouldRender, чтобы компонент отображался только один раз (при его добавлении). И вот что я пытаюсь достичь в своем контроллере Stimulus map-controller.js:

import { Controller } from "stimulus";

import EsriMap from "esri/Map";
import MapView from "esri/views/MapView";

export default class extends Controller {
    connect() {
        var map = new EsriMap({
            basemap: "topo-vector"
        });

        var view = new MapView({
            container: this.element,
            map: map,
            center: [-118.80500, 34.02700], // longitude, latitude
            zoom: 13
        });
    }
}

Изначально я пытался добавить ArcGIS, чтобы он создавался с помощью Webpack (чтобы код, приведенный выше, работал). Однако я столкнулся с проблемой совместимости между javascript api ArcGIS и tailwindcss. ArcGIS не будет компилироваться, потому что возникла проблема с вызовом require('fs').

Вместо того, чтобы обойти проблему require('fs') (что выходит за рамки моего существующего опыта), я решил использовать ArcGIS js черезCDN. Поэтому я попытался настроить ArcGIS с помощью функции конфигурации external в Webpack. Вот мой webpack.js.config файл:

const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const bundleFileName = 'holly';
const dirName = 'Holly/wwwroot/dist';

module.exports = (env, argv) => {
    return {
        mode: argv.mode === "production" ? "production" : "development",
        externals: {
            'esrimap': 'esri/Map',
            'mapview': 'esri/views/MapView'
        },
        entry: ['./Holly/wwwroot/js/app.js', './Holly/wwwroot/css/styles.css'],
        output: {
            filename: bundleFileName + '.js',
            path: path.resolve(__dirname, dirName),
            libraryTarget: "umd"
        },
        module: {
            rules: [{
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader'
                ]
            }]
        },
        plugins: [
            new MiniCssExtractPlugin({
                filename: bundleFileName + '.css'
            })
        ]
    };
};

И вот что я сделал в контроллере стимула:

import { Controller } from "stimulus";

import EsriMap from "esrimap";
import MapView from "mapview";

export default class extends Controller {
    connect() {
        var map = new EsriMap({
            basemap: "topo-vector"
        });

        var view = new MapView({
            container: this.element,
            map: map,
            center: [-118.80500, 34.02700], // longitude, latitude
            zoom: 13
        });
    }
}

Однако в браузере я вижу следующее исключение:

TypeError: esrimap__WEBPACK_IMPORTED_MODULE_1___default.a is not a constructor
    at Controller.connect (map_controller.js:14)
    at Context.connect (context.ts:35)
    at Module.connectContextForScope (module.ts:40)
    at eval (router.ts:109)
    at Array.forEach (<anonymous>)
    at Router.connectModule (router.ts:109)
    at Router.loadDefinition (router.ts:60)
    at eval (application.ts:51)
    at Array.forEach (<anonymous>)
    at Application.load (application.ts:51)

Как вы уже догадались, я достигаю предела своих знаний в области javascript / webpack. Я провел небольшое исследование API ArcGIS javascript и поддерживает ли оно commonjs. По-видимому, он использует Dojo, который поддерживает AMD. Вместо этого я попробовал следующую конфигурацию:

        externals: [{
            'esrimap': {
                commonjs: 'esri/Map',
                commonjs2: 'esri/Map',
                amd: 'esri/Map'
            },
            'mapview': {
                commonjs: 'esri/views/MapView',
                commonjs2: 'esri/views/MapView',
                amd: 'esri/views/MapView'
            }
        }],

Но я получаю ту же ошибку. Я прочитал через веб-пакет документацию - мне не понятно, как мне это настроить. Я делаю что-то в корне неправильно?

1 Ответ

1 голос
/ 07 октября 2019

Так что я решил вместо этого решить проблему со Стимулом.

Ниже приводится ответ на вопрос, предшествующий фразе выше, игнорируя все, что следует. Альтернативное решение, которое должно работать, даже если оно не является ответом на ваш полный вопрос.

Создайте сценарий, предназначенный для вашего компонента Blazor, и просмотрите его или сделайте ссылку на него в своем Pages/_host.cshtml или wwwroot/index.html:

<script>
    window.myComponent = { 
      init: function(options) { 
        require([
          "esri/Map",
          "esri/views/MapView"
        ], function(Map, MapView) {

          var map = new Map({
            basemap: "topo-vector"
          });

          var view = new MapView({
            container: options.containerId,
            map: map,
            center: [-118.71511,34.09042],
            zoom: 11
          });

        }); // end require

       } // end init

     }; // end myComponent
  </script>

И вызовите этот скрипт в своем компоненте, переопределив async Task OnAfterRenderAsync(bool isFirstRender). Вызывайте его, только если isFirstRender установлено на true.

Вы можете вызвать сценарий, введя IJSRuntime и вызвав await InvokeAsync("myComponent.init", "container-id")

Что-то вроде этого (не проверено)

@inject IJSRuntime JSRuntime
<div id="@containerId" data-controller="map"></div>

@code {
    private string containerId = Guid.CreateGuid().ToString("n");
    protected override bool ShouldRender()
    {
        var allowRefresh = false;

        return allowRefresh;
    }

    protected override async Task OnAfterRenderAsync(bool isFirstRender) {
      if (isFirstRender){ 
         await JSRuntime.InvokeAsync("myComponent.init", new { containerId: containerId }) 
      }
    }
}
...